Slide 1

Slide 1 text

Build a web service 2. Upload and watch a video file by Firebase

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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; } } }

Slide 7

Slide 7 text

Implementation Use to upload a file and write an onChange handler. const handleChange = (event: React.ChangeEvent) => { 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); };

Slide 8

Slide 8 text

Implementation Update a metadata when it's published const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); updateMetadata(currentUploadTask.snapshot.ref, { customMetadata: { visibility: "public", }, cacheControl: "public,max-age=86400", }) .then((metadata: FullMetadata) => { router.push(`/videos/${videoId}`); }) .catch(onError); };

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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; };

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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//o?name=. (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:// , it didn't help at all.

Slide 14

Slide 14 text

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);

Slide 15

Slide 15 text

Implementation Render a video {contentType == "unknown" && } {contentType != "unknown" && }

Slide 16

Slide 16 text

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.

Slide 17

Slide 17 text

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.

Slide 18

Slide 18 text

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- emulators:exec 'next dev' --ui

Slide 19

Slide 19 text

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