Upload Files to IPFS with Next.js and Web3.Storage
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 .
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.