node.js 实战——express图片保存到本地或服务器(七牛云、腾讯云、阿里云)

本地

✅ 使用formidable 读取表单内容

bash 复制代码
npm i formidable 

✅ 使用mime-types 获取图片后缀

bash 复制代码
npm install mime-types

✅ js 中提交form表单

javascript 复制代码
 document.getElementById('uploadForm').addEventListener('submit', function(e){

        e.preventDefault();
        const blob =preview._blob;
        if(!blob){
            alert("请先选择并裁剪头像");
            return;
        }
        const formData = new FormData();
        formData.append('file', blob);

        //未裁剪使用代码
        // const file =input.files[0];
        // if(!file) return alert('Please upload a valid image!');
        //
        // let formData = new FormData();
        // formData.append('file', file);

        let xhr = new XMLHttpRequest();
        xhr.open('POST', '/users/upload-avatar');

        xhr.upload.addEventListener('progress', (e) => {
            if(e.lengthComputable){
                let percent = Math.round((e.loaded / e.total) * 100);
                progressWrapper.classList.remove('d-none');
                progressBar.style.width= percent + '%';
                progressBar.innerText = percent + '%';
            }
        })


        xhr.onload =function () {
            if(xhr.status === 200) {
                alert("successfully uploaded!");
            }else{
                alert("fail to upload");
            }
        }
        xhr.send(formData);

    })

✅ express 中routers的js

javascript 复制代码
var express = require('express');
var router = express.Router();
const { IncomingForm } = require('formidable'); // ✅ 注意不是直接 require('formidable')
const path = require('path');
const mime = require('mime-types')


/* GET users listing. */
router.get('/', function(req, res, next) {

  res.render('user', { title: '订餐系统' });
});

router.post('/upload-avatar', function(req, res, next) {
  //创建表单对象
  const form =new IncomingForm({
    multiple:true,
    //设置上传文件的保存目录
    uploadDir:__dirname+'/../public/uploads/',
    //保存文件后缀
    keepExtension:true,
    // 支持多文件
    multiples:true,
    filename:(name,ext,part,form)=>{
      const timestamp = Date.now();
      let extension = mime.extension(part.mimetype) || '';
      return `${timestamp}-${Math.random().toString(36).slice(2)}${extension ? '.' + extension : ''}`;
    }
  });
  //
  form.parse(req,function(err,fields,files){
    if(err){
      next(err);
      return;
    }



    res.json({files,files});

  })

})

module.exports = router;



七牛云

✅ 实现步骤概览

  1. 使用 formidable 获取上传的图片流或临时文件路径
  2. 使用 qiniu SDK 上传到七牛云对象存储
  3. 返回文件 URL 给前端

✅ 安装依赖

bash 复制代码
npm install formidable qiniu

✅ 七牛云配置准备

你需要准备这些信息:

• accessKey

• secretKey

• bucket(空间名称)

• domain(空间绑定的 CDN 域名)

✅ 完整 Express + formidable + 七牛 上传示例

javascript 复制代码
const express = require('express');
const { IncomingForm } = require('formidable');
const fs = require('fs');
const path = require('path');
const qiniu = require('qiniu');

const app = express();

// 七牛配置
const accessKey = '你的AccessKey';
const secretKey = '你的SecretKey';
const bucket = '你的Bucket';
const domain = 'http://你的空间域名'; // 绑定的 CDN 域名(带 http)

// 上传凭证
const mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
const putPolicy = new qiniu.rs.PutPolicy({ scope: bucket });
const uploadToken = putPolicy.uploadToken(mac);

// 七牛配置对象
const config = new qiniu.conf.Config();
// 根据你空间的 Zone 设置,如华东为 Zone_z0
config.zone = qiniu.zone.Zone_z0;

// 表单上传接口
app.post('/upload', (req, res) => {
  const form = new IncomingForm({ multiples: false, keepExtensions: true });

  form.parse(req, (err, fields, files) => {
    if (err) return res.status(500).json({ error: '上传失败' });

    const file = files.images || files.file;
    const localFilePath = file.filepath || file.path; // 不同版本字段不同
    const fileExt = path.extname(file.originalFilename || file.name || '');
    const fileName = `${Date.now()}-${Math.random().toString(36).slice(2)}${fileExt}`;

    const putExtra = new qiniu.form_up.PutExtra();
    const formUploader = new qiniu.form_up.FormUploader(config);

    formUploader.putFile(uploadToken, fileName, localFilePath, putExtra, (respErr, respBody, respInfo) => {
      // 删除本地临时文件(可选)
      fs.unlink(localFilePath, () => {});

      if (respErr) return res.status(500).json({ error: '七牛上传失败' });

      if (respInfo.statusCode === 200) {
        res.json({
          success: true,
          url: `${domain}/${respBody.key}` // 返回七牛图片地址
        });
      } else {
        res.status(respInfo.statusCode).json({ error: respBody });
      }
    });
  });
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

✅ 七牛空间设置建议

设置项 建议
空间类型 公有(或使用私有 + 临时下载链接)
绑定 CDN 域名 用于外链图片地址
设置防盗链 生产环境建议设置 Referer 防盗链

✅ 最终效果

上传成功后,前端将得到形如:

{

"success": true,

"url": "http://cdn.yourdomain.com/1700000000-abc123.jpg"

}

前端就可以将这个 URL 显示为图片头像或多图预览。

腾讯云

如果你要把上传的图片保存到 腾讯云对象存储(COS),可以使用官方的 COS Node.js SDK。

✅ 安装依赖

javascript 复制代码
npm install formidable cos-nodejs-sdk-v5

✅ 腾讯云 COS 准备

你需要准备以下内容:

参数来源 (腾讯云后台)
SecretId 访问管理 - API 密钥管理
SecretKey 同上
Bucket 例如:example-1250000000
Region 如 ap-nanjing

✅ 后端上传图片到腾讯云 COS

javascript 复制代码
const express = require('express');
const { IncomingForm } = require('formidable');
const COS = require('cos-nodejs-sdk-v5');
const fs = require('fs');
const path = require('path');

const app = express();

// 腾讯云配置
const cos = new COS({
  SecretId: '你的SecretId',
  SecretKey: '你的SecretKey',
});

// 上传接口
app.post('/upload', (req, res) => {
  const form = new IncomingForm({ keepExtensions: true });

  form.parse(req, (err, fields, files) => {
    if (err) return res.status(500).json({ error: '上传失败' });

    const file = files.file || files.image || files.images;
    const filePath = file.filepath || file.path;
    const fileExt = path.extname(file.originalFilename || file.name || '');
    const fileName = `uploads/${Date.now()}-${Math.random().toString(36).slice(2)}${fileExt}`;

    // 上传到 COS
    cos.putObject({
      Bucket: '你的Bucket-数字ID',
      Region: 'ap-你的地域',
      Key: fileName,
      Body: fs.createReadStream(filePath),
      ContentLength: fs.statSync(filePath).size,
    }, (err, data) => {
      // 删除临时文件
      fs.unlink(filePath, () => {});

      if (err) return res.status(500).json({ error: 'COS上传失败', detail: err });

      const url = `https://${data.Location}`;
      res.json({ success: true, url });
    });
  });
});

app.listen(3000, () => {
  console.log('Server started on http://localhost:3000');
});

✅ 示例返回

javascript 复制代码
{
  "success": true,
  "url": "https://example-1250000000.cos.ap-nanjing.myqcloud.com/uploads/1700000000-abc123.jpg"
}

🛡️ 注意事项

  • Bucket 名称必须是:- 格式,例如:mybucket-1250000000
  • 记得绑定好 CNAME 或设置跨域(CORS)策略以支持前端访问
  • 腾讯云对象存储会自动生成公网访问链接(基于默认域名)

阿里云

如果你想将上传的图片保存到 阿里云 OSS(对象存储服务),可以使用官方的 ali-oss Node.js SDK。

✅ 安装依赖

javascript 复制代码
npm install formidable ali-oss

✅ 准备阿里云 OSS 配置

你需要这些信息:

项目 获取方式
accessKeyId 阿里云账号控制台 → RAM 用户 → 访问密钥管理
accessKeySecret 同上
bucket OSS 控制台创建的 Bucket 名称
region Bucket 所在地域,如 oss-cn-hangzhou
cdnDomain 推荐绑定自定义 CDN 域名,如 https://img.example.com

✅ 后端 Express 示例

javascript 复制代码
const express = require('express');
const { IncomingForm } = require('formidable');
const OSS = require('ali-oss');
const fs = require('fs');
const path = require('path');

const app = express();

// 阿里云 OSS 配置
const client = new OSS({
  region: 'oss-cn-hangzhou',          // 修改为你的地域
  accessKeyId: '你的AccessKeyId',
  accessKeySecret: '你的AccessKeySecret',
  bucket: '你的Bucket名称',
});

const cdnDomain = 'https://your-cdn.example.com'; // 可选:绑定的自定义域名

app.post('/upload', (req, res) => {
  const form = new IncomingForm({ keepExtensions: true });

  form.parse(req, async (err, fields, files) => {
    if (err) return res.status(500).json({ error: '上传失败' });

    const file = files.file || files.image || files.images;
    const filePath = file.filepath || file.path;
    const ext = path.extname(file.originalFilename || file.name || '');
    const fileName = `uploads/${Date.now()}-${Math.random().toString(36).slice(2)}${ext}`;

    try {
      const result = await client.put(fileName, filePath);
      fs.unlink(filePath, () => {}); // 删除临时文件

      res.json({
        success: true,
        url: cdnDomain ? `${cdnDomain}/${fileName}` : result.url,
      });
    } catch (ossErr) {
      res.status(500).json({ error: 'OSS上传失败', detail: ossErr });
    }
  });
});

app.listen(3000, () => {
  console.log('Server running at http://localhost:3000');
});

✅ 示例上传成功响应

javascript 复制代码
{
  "success": true,
  "url": "https://your-cdn.example.com/uploads/1700000000-abc123.jpg"
}

📝 注意事项

  • 推荐在 OSS 控制台开启跨域(CORS) 支持,方便前端访问
  • 上传后图片即刻可通过公网域名访问
  • 如果你使用的是绑定 CDN 的域名,请确保缓存配置正确(或开启自动刷新)
相关推荐
天天扭码1 天前
前端如何实现RAG?一文带你速通,使用RAG实现长期记忆
前端·node.js·ai编程
hxmmm1 天前
自定义封装 vue多页项目新增项目脚手架
前端·javascript·node.js
濮水大叔1 天前
VonaJS是如何做到文件级别精确HMR(热更新)的?
typescript·node.js·nestjs
小胖霞1 天前
全栈系列(15)github Actions自动化部署前端vue
前端·node.js·github
LYFlied1 天前
【一句话概述】Webpack、Vite、Rollup 核心区别
前端·webpack·node.js·rollup·vite·打包·一句话概述
程序员爱钓鱼1 天前
Node.js 编程实战:MongoDB 基础与 Mongoose 入门
后端·node.js·trae
程序员爱钓鱼1 天前
Node.js 编程实战:MySQL PostgreSQL数据库操作详解
后端·node.js·trae
古韵1 天前
当 API 文档走进编辑器会怎样?
vue.js·react.js·node.js
小胖霞2 天前
企业级全栈项目(14) winston记录所有日志
vue.js·前端框架·node.js
Anita_Sun2 天前
🎨 基础认知篇:打破单线程误区
node.js