BlogNode.js

How to upload files to DigitalOcean Spaces with Node.js

Written by Codemzy on May 29th, 2024

In this blog post, we will create a function to upload a file to DigitalOcean Spaces using Node.js and the AWS SDK.

This post contains affiliate links. If you use these links, I may earn a commission (at no cost to you). I only recommend products I use myself that solve a specific problem. In this post, you are recommended DigitalOcean Spaces, for S3-compatible storage with affordable and predictable pricing. Get started with a $200 credit when you use my referral.

I use DigitalOcean Spaces as my S3-compatible object storage provider. I'm pretty pleased with the performance and price predictability, and I found it easier to get started than going directly with AWS.

I also find uploading files via the web interface even easier, so that's pretty useful!

But this blog post is not about the web interface - it's about using the Spaces API to upload files directly from Node.js.

Luckily for us, uploading files to DigitalOcean Spaces with Node.js is very similar to uploading files to S3, and many of the same libraries will work.

Node.js @aws-sdk set up

Before we can upload files to DigitalOcean Spaces, we need a way to connect to our buckets from Node.js. DigitalOcean Spaces is s3-compatible storage, which means we can use s3 libraries, like the S3Client provided by the AWS SDK.

Let's start by using the latest version (v3.x) of AWS SDK.

If you already have @aws-sdk installed and configured, you can skip to the next section.

If not, here's the code we will use to connect to DigitalOcean Spaces.

📄 /utils/s3.js

import { S3Client } from "@aws-sdk/client-s3";

const s3 = new S3Client({
  endpoint: "https://ams3.digitaloceanspaces.com",
  forcePathStyle: false,
  region: "ams3",
  credentials: {
    accessKeyId: process.env.S3_KEY,
    secretAccessKey: process.env.S3_SECRET
  }
});

Now we are ready to create our uploadFile() function, which will take care of uploading files to DigitalOcean Spaces.

function uploadFile() {
  // we will add the code here
};

Add function arguments

If we want to upload a file, our function will need to take a few arguments.

function uploadFile({ bucket, location, file }) {
  // we will add the code here
};

bucket

Our function will need to know which bucket we want to put the object in. You can have multiple buckets and each bucket you create has its own URL.

You might choose to use multiple buckets for one application, for different use cases. For example, you might want to store application logs and create a bucket with a lifecycle rule that deletes logs after a year. Or create downloads or documents for users that are only stored for a few days - like a temp directory.

location

This will be the location (including the filename) for our object. For example "/user1/project1/first-file.png".

file

This will be the file object. I've kind of based this off the Multer file information object since I was using this middleware to handle user uploads.

But we can use the function without Multer (example later), by passing a file object with the following properties:

let file = {
  mimetype: "application/pdf", // Mime type of the file
  filename: "myfile.pdf", // The name of the file
  buffer: new ArrayBuffer(8), // A Buffer of the entire file
}

Put Object

S3 is object storage, not file storage, so our files will be known as objects from now on!

To upload a file to DigitalOcean Spaces, we are going to put an object in our bucket (so make sure you have already created Spaces a bucket!).

The PutObjectCommand adds an object to a bucket, so we'll use that.

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

function uploadFile({ bucket, location, file }) {
  const command = new PutObjectCommand({
    Key: `${location ? `${location}/` : ""}${file.filename}`,
    Body: file.buffer,
    Bucket: bucket,
    ACL: 'public-read',
    ContentType: file.mimetype,
  });
};

Now we have the command ready. But to actually upload the file to DigitalOcean Spaces, we need our function to send the command.

Because PutObjectCommand is asynchronous, we will turn our function into an async function so we can await the result.

async function uploadFile({ bucket, location, file }) {
  let key = `${location ? `${location}/` : ""}${file.filename}`;
  const command = new PutObjectCommand({
    Key: key,
    Body: file.buffer,
    Bucket: bucket,
    ACL: 'public-read',
    ContentType: file.mimetype,
  });
  await s3.send(command);
  return key;
};

I've also created a new key variable so that I can return the file path in the response (as it might be needed, for example, to let the user know the URL to the file, or to store it in a database).

Uploading files

Here's the final code for our uploadFile() function.

📄 /utils/s3.js

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

// connect to digitalocean spaces
const s3 = new S3Client({
  endpoint: "https://ams3.digitaloceanspaces.com",
  forcePathStyle: false,
  region: "ams3",
  credentials: {
    accessKeyId: process.env.S3_KEY,
    secretAccessKey: process.env.S3_SECRET
  }
});

// upload to digitalocean spaces
async function uploadFile({ bucket, location, file }) {
  let key = `${location ? `${location}/` : ""}${file.filename}`;
  const command = new PutObjectCommand({
    Key: key,
    Body: file.buffer,
    Bucket: bucket,
    ACL: 'public-read',
    ContentType: file.mimetype,
  });
  await s3.send(command);
  return key;
};

export { uploadFile };

And now we can use that function anywhere in our Node.js API when we need to upload files to our DigitalOcean Spaces buckets.

For example, if we have an API endpoint that receives a file buffer handled by Multer middleware:

import { uploadFile } from "../utils/s3.js";

async function handleUpload({ user, file }) {
  let userId = user._id;
  // upload the image
  let path = await uploadFile({ bucket: "user-uploads", location: userId, file });
  return `https://uploads.mywebsite.com/${path}`;
};

And here's how I use it when creating PDF downloads for users:

import { uploadFile } from "../utils/s3.js";
import { getRandomId } from "../utils";

async function createDownload({ user, html }) {
  let userId = user._id;
  // create the pdf
  let buffer = await createPDF(html);
  // upload the file
  let file = { buffer, filename: `${getRandomId()}.pdf`, mimetype: "application/pdf" };
  let filepath = await uploadFile({ bucket: "user-downloads", location: userId, file });
  return `https://downloads.mywebsite.com/${filepath}`;
};

Now you know how to upload files with Node.js, here's how to move files, copy files, and delete files!