本文演示如何在 Node.js + Express + MongoDB 架構下,利用 HTML5 FormData 實現圖片異步上傳,並嵌入 Markdown 編輯器,後端使用 formidable 處理 multipart/form-data 解析與落盤。
form#uploadfile
div.form-group
input#inputfile(type="file" name="inputfile")
p.help-block#upfiletip 只支持 png 和 jpg 格式的圖片上傳
button.btn.btn-success(type="button" onclick="upFile()") 上傳
// 校驗副檔名
function judgePhotoExt(name) {
if (!name.length) { showTip("你沒有選擇任何圖片!!!"); return false; }
var ext = name.substring(name.lastIndexOf('.')).toLowerCase();
if (ext !== '.png' && ext !== '.jpg' && ext !== '.jpeg') {
showTip("只支持 png 和 jpg 格式!!!"); return false;
}
return true;
}
// 異步上傳
function upFile() {
var filename = $("#inputfile").val();
if (!judgePhotoExt(filename)) return;
var formData = new FormData();
var files = $("#inputfile")[0].files;
if (files.length) formData.append("file", files[0]);
$.ajax({
url: '/blog/photo/new',
type: 'POST',
data: formData,
async: false, // 同步上傳,等待返回 URL 嵌入編輯器
cache: false,
contentType: false, // 關鍵:禁止 jQuery 設置 Content-Type,讓瀏覽器自動生成 boundary
processData: false, // 關鍵:禁止 jQuery 將 FormData 序列化為字串
success: function(url) {
var txt = $("#content-textarea").val();
$("#content-textarea").val(txt + "");
$('#imageModal').modal('hide');
},
error: function(err) { console.log(err); }
});
}
關鍵點:contentType: false + processData: false 確保瀏覽器正確發送 multipart/form-data; boundary=---- 請求體。
var oMyForm = new FormData();
oMyForm.append("username", "Groucho");
oMyForm.append("accountnum", 123456); // 數字自動轉字串
oMyForm.append("userfile", fileInput.files[0]); // 文件對象
var oBlob = new Blob(["hey!"], {type: "text/xml"});
oMyForm.append("webmasterfile", oBlob); // Blob 對象
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://foo.com/submitform.php");
xhr.send(oMyForm);
const formidable = require('formidable');
const fs = require('fs');
const uuid = require('uuid');
router.post('/photo/new', (req, res) => {
const form = new formidable.IncomingForm();
form.uploadDir = UPLOAD_PATH; // 臨時目錄
form.keepExtensions = true; // 保留副檔名
form.maxFieldsSize = 4 * 1024 * 1024; // 4MB 限制
form.parse(req, (err, fields, files) => {
if (err) return res.send("上傳失敗");
let urls = [];
for (let key in files) {
let file = files[key];
let ext = '';
switch (file.type) {
case 'image/pjpeg':
case 'image/jpeg': ext = 'jpg'; break;
case 'image/png':
case 'image/x-png': ext = 'png'; break;
}
if (!ext) return res.send("只支持 png 和 jpg 格式");
let saveName = uuid.v1() + '.' + ext;
let savePath = form.uploadDir + saveName;
urls.push(PHOTO_URL + saveName);
fs.renameSync(file.path, savePath); // 移動到永久目錄
}
res.send(urls[0]); // 返回訪問 URL
});
});
FormData 實現零依賴的異步文件構造,配合 contentType/processData: false 繞過 jQuery 默認序列化。formidable 簡化 Node 端 multipart 解析,支援大文件流式寫入臨時目錄再重命名。