$30 off During Our Annual Pro Sale. View Details »

Building a web service. Upload and watch a vide...

Building a web service. Upload and watch a video by Firebase

This is a project to build a web service for sharing indie anime.

The video to explain this slide: https://youtu.be/ICKqN7ys834

The previous slide: https://speakerdeck.com/michaelfreling/google-sign-in-with-firebase-in-react-101

Michael

June 20, 2022
Tweet

More Decks by Michael

Other Decks in Programming

Transcript

  1. Summary 1. Upload a video file to Google Cloud Storage

    by Firebase 2. Watch a video from the file in the Google Cloud Storage 3. Run a Firebase emulator for development
  2. Upload a video of anime 1. It may take minutes

    to upload a video file 1. It cannot upload a file simultaneously with other video metadata Manage permissions for a video file Other users can watch the video only after a user publishes it Use security rules to manage permissions to watch the video 2. Monitor the progress of whether uploading a file is completed Use the uploadBytesResumable function 3. Support only a few types of video files such as MP4
  3. Flow of uploading a video file User Browser Cloud Storage

    loop [Til uploading the file ends] Upload a file Upload a file Upload the file asynchronously Upload the file asynchronously Show a progress Show a progress Enter the metadata of the video Enter the metadata of the video Submit Submit Update the metadata of the video file Update the metadata of the video file Show the video page Show the video page User Browser Cloud Storage
  4. Security rules Rules A user can upload the file and

    watch the file for their videos Other users can watch a video only if it's published Implementation Manage if a video is published or not by its metadata. Define visibility metadata and set public if it's published Store videos under /videos/{userId} The user can read and write their videos Define the file in a storages.rules file created by firebase init CLI firebase deploy --only storage CLI
  5. Security rules implementation rules_version = '2'; service firebase.storage { match

    /b/{bucket}/o { // Only a user can upload their profile picture, but anyone can view it match /videos/{userId}/{videoId} { // True if the user is signed in or the requested data is 'public' function signedInOrPublic() { return resource.metadata.visibility == 'public' || request.auth.uid == userId; } allow read: if signedInOrPublic(); allow write: if request.auth.uid == userId; } } }
  6. Implementation Use <input type="file"> to upload a file and write

    an onChange handler. const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { const file = event.target.files![0]; // ... const contentType = file.type; const id = String(Date.now()); const storageRef = ref(storage, `videos/${auth.id}/${id}.${extension}`); const metadata = { contentType, }; const uploadTask = uploadBytesResumable(storageRef, file, metadata); uploadTask.on("state_changed", onProgress, onError, onComplete); };
  7. Implementation Update a metadata when it's published const handleSubmit =

    (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); updateMetadata(currentUploadTask.snapshot.ref, { customMetadata: { visibility: "public", }, cacheControl: "public,max-age=86400", }) .then((metadata: FullMetadata) => { router.push(`/videos/${videoId}`); }) .catch(onError); };
  8. Error handlings Errors could happen when accessing Google Cloud Storage

    The list of the errors are described in the official Firebase document Classify errors into something that can be handled differently, and decide each action Error code Unexpected? Next possible user actions storage/unauthenticated No Redirect to a Sign in page storage/canceled No No error message should show Others Yes See a system error message. This can happen temporarily due to many reasons
  9. Error handlings The error handling code. Note that a global

    error handling to catch an error should be updated later const onError = (error: StorageError) => { switch (error.code) { case "storage/unauthenticated": redirectToSignInPage(router, redirectReason.REQUIRE_SIGN_IN); return; case "storage/canceled": console.log("Upload a file was canceled by a user"); return; case "storage/quota-exceeded": default: // Unknown error occurred, inspect error.serverResponse setValidationError( "It's temporarily unavailable. Please try it again after a few hours" ); break; } throw error; };
  10. Watch a video Page URL: /videos/{videoId} The URL of a

    video in Cloud Storage: /videos/{userId}/{videoId} The uploaded user id of a video will be stored in a server side It will be handled in the next video Check the content type of a video file not to play something uploaded in a not right way Play only a video which is supported in the service
  11. Error handling Classify errors into something that can happen expectedly

    or not, and decide what a user is supposed to do Error code Unexpected? Next possible user actions storage/object-not- found No User accessed a nonexistent video storage/unauthorized No The video file cannot be accessed at a moment. Maybe the video became private Others Yes See a system error message. This can happen temporarily due to a release of new change
  12. Troubleshootings 1. Failed to upload a video by 404 NOT

    FOUND I forgot to enable Storage on Firebase 2. I failed to upload a file by next error Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://firebasestorage.googleapis.com/v0/b/<bucket>/o?name=<path>. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 400. I switched the browser from Firefox to Google Chrome and resolved it for some reasons I tried to set CORS configurations on the bucket like gsutil cors set cors.json gs://<bucket> , it didn't help at all.
  13. Implementation Watch a video file const videoRef = ref(storage, `videos/${uid}/${videoId}.mp4`);

    getMetadata(videoRef) .then((metadata: FullMetadata) => { if (metadata.contentType == null) { setValidationError("The format of this video is not supported."); return; } if (!(metadata.contentType in supportedFormats)) { setValidationError("The format of this video is not supported."); return; } setContentType(metadata.contentType); }) .catch(onError); getDownloadURL(videoRef) .then((url) => { setUrl(url); }) .catch(onError);
  14. Implementation Render a video <video controls autoPlay style={{ maxWidth: "100vw",

    maxHeight: "100vh" }}> {contentType == "unknown" && <source src={url} />} {contentType != "unknown" && <source src={url} type={contentType} />} </video>
  15. Run an emulator 1. Install a firebase CLI. curl -Lo

    ./firebase_bin https://firebase.tools/bin/linux/v10.9.2 chmod +x ./firebase_bin sudo mv ./firebase_bin /usr/local/bin/firebase There was a reported bug on Firebase CLI version 11 at that time, so I needed to downgrade the version. How to download the specific version of a standalone binary was described in the install script.
  16. Run an emulator 2. Install firebase CLI sudo apt install

    openjdk-18-jre-headless firebase init emulators Run an emulator with a project starting with demo- prefix for a demo project. firebase --project demo-${project-id} emulators:start But I couldn't figure out how to prevent accessing Firebase services not in emulators even if using a demo project.
  17. Run an emulator Access localhost:4000 on a browser and see

    the Firebase services in your emulator. Then run a Nextjs app with a Firebase emulator by next command. firebase --project demo-<project_id> emulators:exec 'next dev' --ui
  18. Next 1. Set up a server side code and store

    a data into DB Probably use MySQL? 2. Authenticate a user on a server side with Firebase Authentication 3. Store the video metadata on a server side