Skip to content

基于nodejs的图片上传

  • 后端插件 npm install multer

filesUtil.js 工具

javascript
const multer = require('multer');
const fs = require("fs");
const path = require('path');

// 上传文件
module.exports.uploadFiles = ({ dir = "./public/temp", key = "file", size = 10000 } = {}) => {
    // 1. 对参数进行解构并设置默认值
    // 2. 设置 multer 的参数,配置 diskStorage,来控制文件存储的位置以及文件名字等
    const storage = multer.diskStorage({
        // 2.1 确定图片存储的位置
        destination: function (req, file, cb) {
            // 当 dir 所对应目录不存在时,则自动创建该文件
            try {
                fs.accessSync(dir);
            } catch (error) {
                fs.mkdirSync(dir);
            }
            cb(null, dir);
        },
        // 2.2 确定图片存储时的名字。(注意:如果使用原名,可能会造成再次上传同一张图片的时候的冲突)
        filename: function (req, file, cb) {
            var changedName = new Date().getTime() + parseInt(Math.random() * 10) + path.extname(file.originalname);
            cb(null, changedName);
        }
    });
    // 3. 配置图片限制
    const limits = {
        // 限制文件大小
        fileSize: 1024 * size,
        // 限制文件数量
        files: 10
    };
    // 4.生成的专门处理上传的一个工具,可以传入 storage、limits 等配置
    const upload = multer({ storage, limits });
    // 5. 返回多文件上传的设置信息(同样可用于单文件上传)
    return upload.array(key);
}

// 移动文件
module.exports.moveFiles = ({ fromPath, toPath, filename } = {}) => {
    if (!filename) {
        console.log('========== 文件移动失败: filename 文件名不能为空 ==========');
        return;
    }
    // 要移动的文件的原路径
    const sourceFile = path.join(fromPath, filename);
    // 判断源文件是否存在
    try {
        fs.accessSync(sourceFile);
    } catch (error) {
        console.log('========== 文件移动失败:' + sourceFile + ' 该文件不存在。==========');
        return;
    }
    // 判断文件要移动的新路径是否存在,如果不存在,则创建
    try {
        fs.accessSync(toPath);
    } catch (error) {
        fs.mkdirSync(toPath);
    }
    // 文件移动后的新路径
    const newFile = path.join(toPath, filename);
    fs.renameSync(sourceFile, newFile);
    return { newPath: newFile };
}

FilesController.js

javascript
//写文件上传的逻辑以及返回
const { uploadFiles,moveFiles } = require('../utils/filesUtil')
const Utils = require('../utils/utils');
const UsersModel = require('../models/UsersModel');
class FilesController {
    fileUp(req, res) {
        const uploading = uploadFiles()
        uploading(req, res, (err) => {
            if (err) {
                res.send({
                    code: 0,
                    msg: '文件上传失败'
                })
            } else {
                //文件上传成功
                //req.files就是文件上传成功后的一些相关的文件信息
                console.log(req.files)
                if (req.files.length > 0) {
                    res.send({
                        code: 1,
                        msg: '文件上传成功',
                        data: `/temp/${req.files[0].filename}`
                    })
                } else {
                    res.send({
                        code: 0,
                        msg: '文件上传失败'
                    })
                }
            }
        })
    }
    //后端修改用户信息的接口
    async updateUserHeadImg(req, res) {
        const bearerToken = req.get('Authorization')
        console.log(bearerToken)
        if (bearerToken) {
            //解码token
            const decode=Utils.verifyToken(bearerToken);
            const {filename} = req.body
            // 
            console.log(filename);
            const fromPath='public/temp/'
            const toPath='public/images/'
            if (filename) {
                //将图片移动到images文件夹下
                try {
                    moveFiles({
                        fromPath,
                        toPath,
                        filename
                    })
                    // 修改用户信息
                    console.log(toPath+filename );
                    const data = await UsersModel.updateOne({ _id: decode._id }, { img:filename });
                    res.send({
                        code: 1,
                        msg: '修改成功',
                    })
                } catch (error) {
                    res.send({
                        code: 0,
                        msg: '头像修改失败',
                    })
                }
            }
        } else {
            res.send({
                code: 0,
                msg: 'token失效'
            })
        }
    }
}
module.exports = new FilesController();
  • 前端利用 文件框以及FormData 构造函数对文件数据进行格式转换,转换成二进制流,以及ajax向后端发数据
javascript
const flag = true;
    // 确认修改
    $("#changeImg").on('click', async function (e) {
        if (flag) {
            confirm("请先选择头像");
            return;
        }
        let filename = $("#headImg").css("background-image");
        filename = filename.substring(filename.lastIndexOf("/") + 1);
        filename = filename.replace(`")`, "");
        let option = {
            url: "/files/updateUserHeadImg",
            data: {
                filename
            }
        }
        const res = await $cn(option);
        console.log(res);
        return
    });
    // 发送文件数据
    $("#fileImg").on('change', async function (e) {
        flag = false;
        // console.dir(this.files[0]);
        // 1.拿文件数据
        const fileInfo = this.files[0]
        // 2.通 FormData 构造函数对文件数据进行格式转换,转换成二进制流
        const fd = new FormData();
        fd.append('file', fileInfo);
        const res = await $upFile(fd);
        $("#headImg").css("background-image", `url(${res})`);
    });

ajax

javascript
// 默认异步  jquery  Promise
export  function $cn({ url, method="post", data, async = true, dataType = "json", timeout = 3000 } = {}) {
    url = URL_NODE_SERVER + url;
    const token=getLocal("token");
    data=JSON.stringify(data);  //  contentType:"application/json"的时候必须串json字符串
    const exclude= /\/login|\/register/;
    if (token==""&&!exclude.test(url)) {
        return {code:-901,msg:"没有token"};
    }
    return  new Promise((resolve,reject) => {
        $.ajax({
            type: method,url: url,data,dataType,async,timeout, // 超时设置 单位毫秒
            contentType:"application/json", //"application/json"
            headers:{
                //将token每次请求的时候放在请求头中,然后需要在token之前拼接'Bearer '
                Authorization:'Bearer ' + token  
            },
            success:(res)=>{
               if (res.data) {
                if( res.data.img){
                    res.data.img=URL_NODE_SERVER+ res.data.img;
                }
               }
               
                resolve(res);
            },
            error:(res)=>{
                reject(res);
            },
        });
    })
}
// 用于文件传输  头像
export  function $upFile(data) {
    const  url = URL_NODE_SERVER + '/files/fileUp';
    const token=getLocal("token");
    const exclude= /\/login|\/register/;
    if (token==""&&!exclude.test(url)) {
        return {code:-901,msg:"没有token"};
    }
    return  new Promise((resolve,reject) => {
        $.ajax({
            type: "post",url: url,data, // 超时设置 单位毫秒
           //禁止jqueryAjax对传输的数据格式进行内部处理
        contentType:false,
        processData:false,
            headers:{
                //将token每次请求的时候放在请求头中,然后需要在token之前拼接'Bearer '
                Authorization:'Bearer ' + token  
            },
            success:(res)=>{
                resolve(URL_NODE_SERVER+res.data);
            },
            error:(res)=>{
                reject(res);
            },
        });
    })
}