本地
? 使用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;
七牛云
? 實現步驟概覽
- 使用 formidable 獲取上傳的圖片流或臨時文件路徑
- 使用 qiniu SDK 上傳到七牛云對象存儲
- 返回文件 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}`;// 上傳到 COScos.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 | 同上 |
bucket | OSS 控制臺創建的 Bucket 名稱 |
region | Bucket 所在地域,如 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 的域名,請確保緩存配置正確(或開啟自動刷新)