掘金 后端 ( ) • 2024-04-24 10:15

起因

最近写了个简单项目,我直接用 Nuxt3 提供的 server 目录直接进行 API 开发,感觉比较方便。

写图片上传时却找不到方法,尝试许久后知道了如何解决,特此记录一下,方便以后用。

API

这里我用到了 formidable 这个包; 安装就不说了,简单说说如何配置。

首先做点简单的,配置一下 上传路径 以及 自动创建上传目录,确保项目启动前路径存在

import fs from "fs";

// 上传路径
const uploadDir = "./public/img/uploads";

// 创建上传目录
fs.stat(uploadDir, (err, stats) => {
    if (err) {
        fs.mkdir(uploadDir, (err) => {
            if (err) {
                console.error("创建图片目录失败", err);
            }
        });
    }
});

再来进行 formidable 配置,这里我配置了我目前要用的一些配置,例如:多文件、大小限制、扩展名保留、文件重命名、文件过滤

// 上传配置
const form = formidable({
    uploadDir,
    multiples: true, // 支持多文件
    maxFileSize: 5 * 1024 * 1024, // 5MB 大小限制
    keepExtensions: true, // 保留文件扩展名
    // 文件名重命名
    filename: (name: string, ext: string, part, form) => {
        return `${name}-${new Date().getTime()}${ext}`;
    },
    // 文件过滤
    filter: (part) => {
        // 判断文件名是否为图片格式
        if (part.originalFilename?.match(/\.(jpg|jpeg|png)$/i)) {
            return true;
        }
        return false;
    }
});

配置好之后,就可以使用 form 去调用 parse 方法进行文件保存

form.parse(event.node.req, (err, fields, files: any) => {
    if (err) {
        // error 处理
    }
    
    // 成功处理 files
});

直接放出整个 API 代码:

/*
 * @Author: N0ts
 * @Date: 2024-03-12 16:33:26
 * @Description: 文件上传
 * @FilePath: \bto\server\api\manage\file\index.post.ts
 * @Mail:[email protected]
 */
import ResponseStatus from "@/server/type/ResponseStatus";
import formidable from "formidable";
import fs from "fs";

// 上传路径
const uploadDir = "./public/img/uploads";

// 创建上传目录
fs.stat(uploadDir, (err, stats) => {
    if (err) {
        fs.mkdir(uploadDir, (err) => {
            if (err) {
                console.error("创建图片目录失败", err);
            }
        });
    }
});

export default defineEventHandler(async (event) => {
    // 上传配置
    const form = formidable({
        uploadDir,
        multiples: true, // 支持多文件
        maxFileSize: 5 * 1024 * 1024, // 5MB 大小限制
        keepExtensions: true, // 保留文件扩展名
        // 文件名重命名
        filename: (name: string, ext: string, part, form) => {
            return `${name}-${new Date().getTime()}${ext}`;
        },
        // 文件过滤
        filter: (part) => {
            // 判断文件名是否为图片格式
            if (part.originalFilename?.match(/\.(jpg|jpeg|png)$/i)) {
                return true;
            }
            return false;
        }
    });

    function upload() {
        return new Promise((resolve, reject) => {
            form.parse(event.node.req, (err, fields, files: any) => {
                if (err) {
                    if (err.message.includes("options.maxTotalFileSize")) {
                        return reject("图片超出限制!最大限制 5MB");
                    }
                    return reject(err);
                }

                const result = [];
                for (const key in files) {
                    result.push({
                        name: files[key][0].newFilename,
                        url: "/img/uploads/" + files[key][0].newFilename
                    });
                }
                resolve(result);
            });
        });
    }

    try {
        const data = await upload();
        return ResponseStatus.OK(data);
    } catch (err: any) {
        return ResponseStatus.ERROR(err);
    }
});

这里我把上传操作写成了异步函数,方便在外面直接进行 return 结果。 成功后我将 files 的数据 push 到了数组,方便前端使用,也可根据自己业务处理数据。 调用测试: PixPin_2024-04-23_23-56-45.png

文件超出大小限制: image.png

完结撒花~