You will learn, how to upload files using React, NextJs and Fetch API.
First of all, let’s create NextJs application by running:
yarn create next-app --typescript
After the installation is complete, run yarn dev to start the development server on http://localhost:3000 .
If you open the page, you will see something similar to the following:
Now let's install the packages, which will help us to parse form data:
yarn add -D formidable @types/formidable
Creating the form
We will create small form, where we can upload files. I will simplify the code to make it more clear.
Go to pages/index.js
and replace all the default code with the following:
import type { NextPage } from 'next'
import React from "react";
const Home: NextPage = () => {
const [isLoading, setIsLoading] = React.useState(false);
const inputFileRef = React.useRef<HTMLInputElement | null>(null);
const handleOnClick = async (e: React.MouseEvent<HTMLInputElement>) => {
/* Prevent form from submitting by default */
e.preventDefault();
/* If file is not selected, then show alert message */
if (!inputFileRef.current?.files?.length) {
alert('Please, select file you want to upload');
return;
}
setIsLoading(true);
/* Add files to FormData */
const formData = new FormData();
Object.values(inputFileRef.current.files).forEach(file => {
formData.append('file', file);
})
/* Send request to our api route */
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
const body = await response.json() as { status: 'ok' | 'fail', message: string };
alert(body.message);
if (body.status === 'ok') {
inputFileRef.current.value = '';
// Do some stuff on successfully upload
} else {
// Do some stuff on error
}
setIsLoading(false);
};
return (
<form>
<div>
<input type="file" name="myfile" ref={inputFileRef} multiple />
</div>
<div>
<input type="submit" value="Upload" disabled={isLoading} onClick={handleOnClick} />
{isLoading && ` Wait, please...`}
</div>
</form>
)
}
export default Home
Here we are using multiple files uploading. If you want to upload only one file, remove multiple
from input:file
:
<input type="file" name="myfile" ref={inputFileRef} />
API Route
Let’s create an endpoint, where we will process the uploaded files.
Create the filepages/api/upload.ts
:
import type { NextApiRequest, NextApiResponse } from 'next'
import { promises as fs } from "fs";
import path from "path";
import formidable, { File } from 'formidable';
/* Don't miss that! */
export const config = {
api: {
bodyParser: false,
}
};
type ProcessedFiles = Array<[string, File]>;
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
let status = 200,
resultBody = { status: 'ok', message: 'Files were uploaded successfully' };
/* Get files using formidable */
const files = await new Promise<ProcessedFiles | undefined>((resolve, reject) => {
const form = new formidable.IncomingForm();
const files: ProcessedFiles = [];
form.on('file', function (field, file) {
files.push([field, file]);
})
form.on('end', () => resolve(files));
form.on('error', err => reject(err));
form.parse(req, () => {
//
});
}).catch(e => {
console.log(e);
status = 500;
resultBody = {
status: 'fail', message: 'Upload error'
}
});
if (files?.length) {
/* Create directory for uploads */
const targetPath = path.join(process.cwd(), `/uploads/`);
try {
await fs.access(targetPath);
} catch (e) {
await fs.mkdir(targetPath);
}
/* Move uploaded files to directory */
for (const file of files) {
const tempPath = file[1].filepath;
await fs.rename(tempPath, targetPath + file[1].originalFilename);
}
}
res.status(status).json(resultBody);
}
export default handler;
Congratulations, we’re done! You can test your form!
All files will be saved in directory /uploads
inside your project root.
Use Api route as proxy
Here I suggest you to assume, that we need to transfer our files somewhere else to another server, because we don’t want to store them and doing difficult operations on files.
Add some new packages:
yarn add -D form-data node-fetch
Change you /pages/api/upload.ts to
:
import type { NextApiRequest, NextApiResponse } from 'next'
import fs from "fs";
import fetch from "node-fetch";
import FormData from 'form-data';
import formidable, { File } from 'formidable';
export const config = {
api: {
bodyParser: false,
}
};
type ProcessedFiles = Array<[string, File]>;
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
let status = 200,
resultBody = { status: 'ok', message: 'Files were uploaded successfully' };
/* Get files using formidable */
const files = await new Promise<ProcessedFiles | undefined>((resolve, reject) => {
const form = new formidable.IncomingForm();
const files: ProcessedFiles = [];
form.on('file', function (field, file) {
files.push([field, file]);
})
form.on('end', () => resolve(files));
form.on('error', err => reject(err));
form.parse(req, () => {
//
});
}).catch(e => {
console.log(e);
status = 500;
resultBody = {
status: 'fail', message: 'Upload error'
}
});
if (files?.length) {
/* Add files to FormData */
const formData = new FormData();
for (const file of files) {
formData.append(file[0], fs.createReadStream(file[1].filepath));
}
/* Send request to another server */
const response = await fetch('PATH_TO_ANOTHER_SERVER', {
headers: formData.getHeaders(),
method: 'POST',
body: formData
});
// Do anything you need with response
}
res.status(status).json(resultBody);
}
export default handler;
Don’t forget to replace PATH_TO_ANOTHER_SERVER
.
This is it, enjoy! Full repo https://github.com/gapon2401/upload-files-nextjs
Top comments (0)