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

news2025/5/13 13:03:23

本地

✅ 使用formidable 读取表单内容

npm i formidable 

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

npm install mime-types

✅ js 中提交form表单

 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

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 给前端

✅ 安装依赖

npm install formidable qiniu

✅ 七牛云配置准备

你需要准备这些信息:
• accessKey
• secretKey
• bucket(空间名称)
• domain(空间绑定的 CDN 域名)

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

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。

✅ 安装依赖

npm install formidable cos-nodejs-sdk-v5

✅ 腾讯云 COS 准备

你需要准备以下内容:

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

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

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');
});

✅ 示例返回

{
  "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。

✅ 安装依赖

npm install formidable ali-oss

✅ 准备阿里云 OSS 配置

你需要这些信息:

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

✅ 后端 Express 示例

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');
});

✅ 示例上传成功响应

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

📝 注意事项

  • 推荐在 OSS 控制台开启跨域(CORS) 支持,方便前端访问
  • 上传后图片即刻可通过公网域名访问
  • 如果你使用的是绑定 CDN 的域名,请确保缓存配置正确(或开启自动刷新)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2374675.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Shadertoy着色器移植到Three.js经验总结

Shadertoy是一个流行的在线平台,用于创建和分享WebGL片段着色器。里面有很多令人惊叹的画面,甚至3D场景。本人也移植了几个ShaderToy上的着色器。本文将详细介绍移植过程中需要注意的关键点。 1. 基本结构差异 想要移植ShaderToy的shader到three.js&am…

电脑端音乐播放器推荐:提升你的听歌体验!

在快节奏的职场环境中,许多上班族都喜欢用音乐为工作时光增添色彩。今天要分享的这款音乐工具,或许能为你的办公时光带来意想不到的惊喜。 一、软件介绍-澎湃 澎湃音乐看似是个普通的播放器,实则藏着强大的资源整合能力。左侧功能栏清晰陈列着…

VIC-2D 7.0 为平面样件机械试验提供全视野位移及应变数据软件

The VIC-2D系统是一个完全集成的解决方案,它基于优化的相关算法为平面试样的力学测试提供非接触、全场的二维位移和应变数据,可测量关注区域内的每个像素子集的面内位移,并通过多种张量选项计算全场应变。The VIC-2D 系统可测量超过 2000%变形…

一周学完计算机网络之三:1、数据链路层概述

简单的概述 数据链路层是计算机网络体系结构中的第二层,它在物理层提供的基本服务基础上,负责将数据从一个节点可靠地传输到相邻节点。可以将其想象成一个负责在两个相邻的网络设备之间进行数据 “搬运” 和 “整理” 的 “快递中转站”。 几个重要概念…

单片机-STM32部分:13-1、蜂鸣器

飞书文档https://x509p6c8to.feishu.cn/wiki/V8rpwIlYIiEuXLkUljTcXWiKnSc 一、应用场景 大部分的电子产品、家电(风扇、空调、电水壶)都会有蜂鸣器,用于提示设备的工作状态 二、原理 蜂鸣器是一种将电信号转换为声音信号的器件&#xff0…

动态IP技术赋能业务创新:解锁企业数字化转型新维度

在数字经济高速发展的今天,IP地址已不再是简单的网络标识符,而是演变为支撑企业数字化转型的核心基础设施之一。动态IP技术凭借其灵活、高效、安全的特性,正在重塑传统业务模式,催生出诸多创新应用场景。本文将深入剖析动态IP的技…

TDengine 在金融领域的应用

简介 金融行业正处于数据处理能力革新的关键时期。随着市场数据量的爆炸式增长和复杂性的日益加深,金融机构面临着寻找能够高效处理大规模、高频次以及多样化时序数据的大数据处理系统的迫切需求。这一选择将成为金融机构提高数据处理效率、优化交易响应时间、提高…

OSCP - Hack The Box - Sau

主要知识点 CVE-2023-27163漏洞利用systemd提权 具体步骤 执行nmap扫描,可以先看一下55555端口 Nmap scan report for 10.10.11.224 Host is up (0.58s latency). Not shown: 65531 closed tcp ports (reset) PORT STATE SERVICE VERSION 22/tcp o…

QT6 源(93)篇三:阅读与注释共用体类 QVariant 及其源代码,本类支持比较运算符 ==、!=。

(9) 本类支持比较运算符 、! : 可见, QString 类型里可存储多个 unicode 字符,即使只存储一个 unicode 字符也不等于 QChar。 (10)本源代码来自于头文件 qvariant . h : #ifndef Q…

Maven私服搭建与登录全攻略

目录 1.背景2.简介3.安装4.启动总结参考文献 1.背景 回顾下maven的构建流程,如果没有私服,我们所需的所有jar包都需要通过maven的中央仓库或者第三方的maven仓库下载到本地,当一个公司或者一个团队所有人都重复的从maven仓库下载jar包&#…

力扣210(拓扑排序)

210. 课程表 II - 力扣(LeetCode) 这是一道拓扑排序的模板题。简单来说,给出一个有向图,把这个有向图转成线性的排序就叫拓扑排序。如果有向图中有环就没有办法进行拓扑排序了。因此,拓扑排序也是图论中判断有向无环图…

C++ asio网络编程(5)简单异步echo服务器

上一篇文章:C asio网络编程(4)异步读写操作及注意事项 文章目录 前言一、Session类1.代码2.代码详解3.实现Session类1.构造函数2.handle_read3.介绍一下boost的封装函数和api4.handle_write 二、Server类1.代码2.代码思路详解 三、客户端四、运行截图与流程图 前言 提示&…

【机器人】复现 UniGoal 具身导航 | 通用零样本目标导航 CVPR 2025

UniGoal的提出了一个通用的零样本目标导航框架,能够统一处理多种类型的导航任务。 支持 对象类别导航、实例图像目标导航和文本目标导航,而无需针对特定任务进行训练或微调。 本文分享UniGoal复现和模型推理的过程~ 查找沙发,模…

spring中的@PropertySource注解详解

一、核心功能与作用 PropertySource是Spring框架中用于加载外部配置文件的核心注解,主要作用是将属性文件(如.properties、.yml)的键值对加载到Spring的Environment环境中,实现配置与代码的解耦。其核心价值包括: 外部…

二极管钳位电路——Multisim电路仿真

目录 二极管钳位电路 2.1 二极管正向钳位电路 二极管压降测试 2.1.1 二极管正向钳位电路图 2.1.2 二极管正向钳位工作原理 2.2 二极管负向钳位电路 2.2.1 二极管负向钳位电路图 2.2.2 二极管负向钳位工作原理 二极管正向反向钳位仿真电路实验结果 2.3 二极管顶部钳位…

suricata增加单元测试编译失败

一、环境 $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.5 LTS Release: 22.04 Codename: jammysuricata: suricata7.0.5 IDE: vscode 二、背景 在suricata中开发了某个功能后,增加unittest时,…

高并发场景下的BI架构设计:衡石分布式查询引擎与缓存分级策略

在电商大促、金融交易时段或IoT实时监控场景中,企业BI系统常面临瞬时万级并发查询的冲击——运营团队需要实时追踪GMV波动,风控部门需秒级响应欺诈检测,产线监控需毫秒级反馈设备状态。传统单体架构的BI系统在此类场景下极易崩溃,…

鱼眼摄像头(一)多平面格式 单缓冲读取图像并显示

鱼眼摄像头(一)多平面格式 单缓冲读取图像并显示 1.摄像头格式 1. 单平面格式(Single Plane):各通道数据保存在同一个平面(缓冲),图像数据按行连续存储a. mjpeg,yuyv等…

机器学习笔记——特征工程

大家好,这里是好评笔记,公主号:Goodnote,专栏文章私信限时Free。本笔记介绍机器学习中常见的特征工程方法、正则化方法和简要介绍强化学习。 文章目录 特征工程(Fzeature Engineering)1. 特征提取&#xff…

A Survey of Learning from Rewards:从训练到应用的全面剖析

A Survey of Learning from Rewards:从训练到应用的全面剖析 你知道大语言模型(LLMs)如何通过奖励学习变得更智能吗?这篇论文将带你深入探索。从克服预训练局限的新范式,到训练、推理各阶段的策略,再到广泛…