Google Drive Folder Setup and SvelteKit Upload/Rename Endpoints

In the last blog post I set up a service account to authenticate for Google Drive's API, encrypted the resulting .json file, and set up functionality to decrypt said file to use for the purpose of actual authentication.

In this post I will show you how to set up the folder in Google Drive, as well as how to share it correctly, and then add SvelteKit endpoints for handling file uploads and saving them in the necessary folder.

Google Drive Folder Setup

The first thing you need to do is go to drive.google.com/drive/my-drive and create a folder where the files will be stored, which I will set name 'Hashnode File Upload'. You'll then want to open that folder and grab the ID of the folder, which should show up after the /folders/ location in the URL. Screen Shot 2022-06-22 at 11.41.29 AM.png Copy this value and store it in the previously noted .env file as VITE_GOOGLE_DRIVE_FOLDER_ID. After the encryption information and this, our .env file should look like

VITE_SERVICE_ENCRYPTION_IV=t8bcKDaNPRSGH3no
VITE_SERVICE_ENCRYPTION_KEY=hiHa4OLm9xLchadg
VITE_GOOGLE_DRIVE_FOLDER_ID=1StDQlW29iO72tlq6APa8DXT2hegx742l

Sharing Capabilities

In order to allow for the created service account to upload to this folder, you also need to add the email address set up in the service account .json file as an editor to the folder. To remind you, the email address in use for this project would be file-upload@sample-project-for-hashnode.iam.gserviceaccount.com Screen Shot 2022-06-21 at 5.30.12 PM.png Go back to the parent folder of the one for which you grabbed the ID and right-click on the folder, and select "Share" Screen Shot 2022-06-22 at 2.59.18 PM.png Then you simply need to add this email address as an editor, and click "Share" Screen Shot 2022-06-22 at 3.02.43 PM.png

SvelteKit File Upload Endpoint

Now that this is done, I will create a file within the src/routes folder to handle file uploads.

// /src/routes/upload-files.js

import { service } from '$lib/service';

export async function post({ request }) {
    const body = request.body;
    const drive = service();
    const folderId = import.meta.env.VITE_GOOGLE_DRIVE_FOLDER_ID;
    const file = await drive.files.create({
        resource: { parents: [folderId] },
        media: { mimeType: 'application/pdf', body },
        fields: 'id'
    });
    const { id } = file.data;

    return { statusCode: 200, body: { id } };
}

Let's look a bit into what's happening with this function.

  • Since I want to upload a file instead of handle JSON I'm using request.body instead of the more typical await request.json()
  • The drive and folderId lines are fairly self-explanatory, with the service function coming from what was built in the last post
  • The methodology of uploading is done via the await drive.files.create method. The argument for this function was pulled from this post with some slight modifications.
    • I don't need the name because I'm going to be creating a separate function to rename uploaded files
    • Since I'm not using the file system, I didn't use the fs package or anything like that. The request.body itself is a readable stream of just the file contents so I just use that as the body.
    • I'm assuming that this is for a PDF but this can be used for other file types (for example image/*).
  • The id that is returned from file.data and returned from the function itself is for the purpose of eventually renaming the file.
    • The reason I'm handling the name in this way is because I don't know how to access the file name from the readable stream itself

SvelteKit Rename File Endpoint

If you go look at the uploaded file you'll see that the name simply reads "undefined". Like I said, I don't know how to grab the file name from the readable stream, but what I've done instead is return the id from the uploaded file, which we will use in a newly-created /src/routes/rename-file.js endpoint.

// /src/routes/rename-file.js

import service from '$lib/service';

export async function post({ request }) {
    const data = await request.json();
    const drive = service();
    const { fileId, ...body } = data;
    await drive.files.update({ fileId, resource: body });
    return { statusCode: 204 };
}

This is a fairly simple function as we can see. I'm simply extracting the fileId from the request data, and storing the remaining information as a constant called body using the JavaScript spread operator.

Summary

That's it for this short blog post. In the next one I'll work on the in-component functionality where we send any/all files to these routes for upload and renaming.