Upload Files to IPFS with Next.js and Web3.Storage

Sarah SchwartzSarah Schwartz
3 min read

What is IPFS?

IPFS stands for InterPlanetary File System. It's a decentralized peer-to-peer file storage network that anyone can use to store files and data. IPFS was developed by Protocol Labs.

What is Web3.Storage?

Web3.Storage is a service that provides a Javascript SDK for developers to easily upload files to and interact with IPFS. To use Web3.Storage, you must create an account on their website and create an API token.

Setup

Create a new Next.js app, or open an existing project. Add the project dependencies below:

$ npm install web3.storage next-connect multiparty
--OR--
$ yarn add web3.storage next-connect multiparty

Create a .env.local file in your project root directory, and add your Web3.Storage API token as shown below.

WEB3STORAGE_TOKEN=yourAPIToken

Creating a Form

Create a new page and add the code below.

import { useState } from "react";

export default function UploadForm() {
  const [filesName, setFilesName] = useState("");
  const [filesDescription, setFilesDescription] = useState("");
  const [files, setFiles] = useState(null);

  async function handleSubmit(e) {
    e.preventDefault();
    let filesArray = [];
    for (let i = 0; i < files.length; i++) {
      filesArray.push(files[i]);
    }
    await uploadFiles(filesArray);
  }

  async function uploadFiles(filesArray) {
    let formData = new FormData();
    filesArray.forEach((file, index) => {
      formData.append(`file${index}`, file);
    });
    formData.append("name", filesName);
    formData.append("description", filesDescription);
    await uploadToIPFS(formData);
  }

  async function uploadToIPFS(formData) {
    try {
      const response = await fetch("/api/upload", {
        method: "POST",
        body: formData,
      });
      if (response.status !== 200) {
        console.log("ERROR", response);
      } else {
        console.log("Form successfully submitted!");
        let responseJSON = await response.json();
        console.log("CID:", responseJSON.cid);
        return responseJSON.cid;
      }
    } catch (error) {
      alert(
        `Oops! Something went wrong. Please refresh and try again. Error ${error}`
      );
    }
  }

  return (
    <div>
      <section>
        <form onSubmit={handleSubmit}>
          <div>
            <label htmlFor="files-name">Files Name</label>
            <div>
              <input
                id="files-name"
                name="files-name"
                type="text"
                required
                value={filesName}
                onChange={(e) => setFilesName(e.target.value)}
              />
            </div>
          </div>

          <div>
            <label htmlFor="files-description">Files Description</label>
            <div>
              <input
                id="files-description"
                name="files-description"
                type="text"
                required
                value={filesDescription}
                onChange={(e) => setFilesDescription(e.target.value)}
              />
            </div>
          </div>

          <div>
            <label htmlFor="files">Project Files</label>
            <div>
              <input
                type="file"
                id="files"
                multiple
                required
                onChange={(e) => {
                  setFiles(e.target.files);
                }}
              />
            </div>
          </div>

          <div>
            <button type="submit">Upload Files</button>
          </div>
        </form>
      </section>
    </div>
  );
}

Uploading Files with Web3.Storage

Create a new file in your pages/api/ folder called upload.js. This is where your app will handle the form data submitted.

import nextConnect from "next-connect";
import multiparty from "multiparty";
import { Web3Storage, File, getFilesFromPath } from "web3.storage";
const fs = require("fs");
const { resolve, join, dirname } = require("path");

const handler = nextConnect();

handler.use((req, res, next) => {
  const form = new multiparty.Form();

  form.parse(req, function (err, fields, files) {
    req.body = fields;
    req.files = files;
    next();
  });
});

handler.post(async (req, res) => {
  try {
    const files = await makeFileObjects(req.body, req.files);
    const cid = await storeFiles(files);

    return res.status(200).json({ success: true, cid: cid });
  } catch (err) {
    return res
      .status(500)
      .json({ error: "Error storing the file", success: false });
  }
});

export const config = {
  api: {
    bodyParser: false,
  },
};

export default handler;

async function storeFiles(files) {
  const client = makeStorageClient();
  try {
    const cid = await client.put(files);
    return cid;
  } catch (error) {
    console.log("ERROR", error);
  }
}

async function getNewPath(item) {
  if (item[0].originalFilename && item[0].originalFilename !== "") {
    const filePath = resolve(process.cwd(), item[0].path);
    const newPath = join(dirname(filePath), item[0].originalFilename);
    await fs.promises.rename(filePath, newPath);
    return newPath;
  }
}

async function makeFileObjects(text, myFiles) {
  let files;
  const buffer = Buffer.from(JSON.stringify(text));
  for (let item of Object.values(myFiles)) {
    let newPath = await getNewPath(item);
    if (!files) {
      files = await getFilesFromPath(newPath);
    } else {
      let newFiles = await getFilesFromPath(newPath);
      files = [...files, ...newFiles];
    }
  }

  files.push(new File([buffer], "data.json"));

  return files;
}

function makeStorageClient() {
  return new Web3Storage({ token: process.env.WEB3STORAGE_TOKEN });
}

Testing It Out

Try uploading a couple images with your form. If it was successful, the CID (content ID) should be logged to your console. You can view content uploaded to IPFS by looking up this CID in a public IPFS gateway like ipfs.io/ipfs/YOUR_CID . You can see an example uploaded with this code here: https://ipfs.io/ipfs/bafybeigpmpy3keviojy43fc66js3mpemhdnv2ifi4bnpxbqtxfbv5nuksq .

12
Subscribe to my newsletter

Read articles from Sarah Schwartz directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sarah Schwartz
Sarah Schwartz

I'm a self-taught web developer passionate about web3, learning new things, solving puzzles, and helping others.