您将学习如何使用 React、NextJs 和 Fetch API 上传文件。


首先,让我们通过运行来创建 NextJs 应用程序:

yarn create next-app --typescript

进入全屏模式 退出全屏模式

安装完成后,在http://localhost:3000上运行 yarn dev 启动开发服务器。

如果您打开该页面,您将看到类似于以下内容的内容:

[NextJs 欢迎屏幕](https://res.cloudinary.com/practicaldev/image/fetch/s--cW0rxLL6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/nd0yuu2h17ytdnpb6790.png)

现在让我们安装这些包,这将帮助我们解析表单数据:

yarn add -D formidable @types/formidable

进入全屏模式 退出全屏模式


创建表格

我们将创建小表单,我们可以在其中上传文件。我将简化代码以使其更清晰。

转到pages/index.js并将所有默认代码替换为以下内容:

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

进入全屏模式 退出全屏模式

这里我们使用多个文件上传。如果您只想上传一个文件,请从input:file中删除multiple:

<input type="file" name="myfile" ref={inputFileRef} />

进入全屏模式 退出全屏模式


API路由

让我们创建一个端点,我们将在其中处理上传的文件。

创建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;

进入全屏模式 退出全屏模式

恭喜,我们完成了!你可以测试你的表格!

所有文件都将保存在项目根目录内的目录/uploads中。


使用Api路由做代理

在这里,我建议您假设,我们需要将文件传输到其他地方到另一台服务器,因为我们不想存储它们并对文件进行困难的操作。

添加一些新包:

yarn add -D form-data node-fetch

进入全屏模式 退出全屏模式

改变你/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;

进入全屏模式 退出全屏模式

不要忘记替换PATH_TO_ANOTHER_SERVER


就是这样,享受!完整回购https://github.com/gapon2401/upload-files-nextjs

Logo

React社区为您提供最前沿的新闻资讯和知识内容

更多推荐