访问Express中的文件上载表单数据



如何访问API中的文件?

我阅读了其他解决方案,它们都需要npm包来解决这个问题。我想知道为什么我不能做香草。此外,答案是旧的,建议使用身体解析器,它现在与Express捆绑在一起

我希望解决方案是香草JS,以便更好地理解流程。

客户端

async function uploadFile(file) {
let formData = new FormData();
formData.append("file", file);
let res = await fetchPostFile("/api/files", formData);
}

提取

export async function fetchPostFile(url, formData) {
try {
let result = await (
await fetch(url, {
method: "POST",
withCredentials: true,
credentials: "include",
headers: {
Authorization: localStorage.getItem("token"),
Accept: "application/json",
"Content-type": "multipart/form-data",
},
body: formData,
})
).json();
return result;
} catch (err) {
return err;
}
}

api

router.post("/api/files", async function (req, res, next) {
try {
console.log(req.file);          // undefined
console.log(req.files);         // undefined
console.log(req.body);          // {}
} catch (err) {
next(err);
} finally {
req.connection.release();
}
});

为什么req.body是空的?请帮我理解我做错了什么。

首先需要使用multer包来处理express中的multipart/form-data。您必须将它用作中间件来设置文件的字段名。作为参数传递给single()函数的名称必须与客户端附加的名称匹配。

const multer = require('multer')
router.post("/api/files", multer().single('file'), async function (req, res, next) {
try {
console.log(req.file)
} catch (err) {
next(err)
} finally {
req.connection.release()
}
});

下面是整个文件管理API。

我使用的是busboy,这是multer在引擎盖下使用的。我发现它更容易使用。

const express = require("express");
const router = express.Router();
const config = require("../../config");
const busboy = require("busboy");
const fs = require("fs");
const SHA256 = require("crypto-js/sha256");
let filesFolderPath = config.paths.files;
router.get("/api/records/:recordUid/files/:fieldUid", async (req, res, next) => {
try {
let { recordUid, fieldUid } = req.params;
let query = `
select 
rdf.*,
r.uid recordUid,
round(sizeBytes / 1024, 0) sizeKb,
round(sizeBytes / 1024 / 1024, 0) sizeMb
from recordDataFile rdf
left join record r on r.id = rdf.recordId
left join field f on f.id = rdf.fieldId
where 
r.uid = ?
and f.uid = ?;
`;
let rows = await req.pool.query(query, [recordUid, fieldUid]);
res.status(200).send(rows);
} catch (err) {
next(err);
}
});
router.get("/api/files/:hash", async (req, res, next) => {
try {
let { hash } = req.params;
let query = `
select *
from recordDataFile
where hash = ?
`;
let rows = await req.pool.query(query, [hash]);
let fileData = rows[0];
res.download(fileData.path);
} catch (err) {
next(err);
}
});
router.post("/api/files", async (req, res, next) => {
try {
let bb = busboy({
headers: req.headers,
defCharset: "utf8",
limits: {
fileSize: 20 * 1024 * 1024, // 20 mb
files: 5,
},
});
let fields = {};
// Get any text values
bb.on("field", (fieldname, val, fieldnameTruncated, valTruncated) => {
console.log(fieldname, val);
fields[fieldname] = val;
});
// Read file stream
bb.on("file", (fieldname, fileStream, filename, encoding, mimetype) => {
// Prevents hieroglyphs from cyrillic
let originalName = Buffer.from(filename.filename, "latin1").toString("utf8");
let nameParts = originalName.split(".");
let extension = nameParts[nameParts.length - 1]; // without the . from .jpeg
// IMPORTANT!!! FILE NAME CAN'T HAVE SPACES, it won't save properly!!!
let hash = SHA256(`${+new Date()}${originalName}`).toString();
// Absolute path to file
let filePath = `${filesFolderPath}${hash}`;
// Open writeable stream to path
let writeStream = fs.createWriteStream(filePath);
// Pipe the file to the opened stream
fileStream.pipe(writeStream);
// Check for errors
writeStream.on("error", (err) => {
console.log("writeStream", err);
});
// Writing done, stream closed
writeStream.on("close", async (err) => {
// console.log("closing + SQL");
if (err) {
console.log("closing error");
return;
}
let query = `
insert into recordDataFile
(
recordId,
fieldId,
name,
extension,
hash,
path,
sizeBytes,
userId,
created
)
values
(
(select id from record where uid = ?),
(select id from field where uid = ?),
?,
?,
?,
?,
?,
?,
now()
);
`;
let sizeBytes = fs.statSync(filePath).size;
await req.pool.query(query, [fields.recordUid, fields.fieldUid, originalName, extension, hash, filePath, sizeBytes, req.userId]);
// record updated. send notification?
await req.pool.query(`update record set updated = now(), updatedByUserId = ? where uid = ?`, [req.userId, fields.recordUid]);
});
});
bb.on("finish", () => {
res.status(200).send({ success: true });
});
req.pipe(bb); // Hooks the streams together. Without it, you're not feeding busboy any data to parse.
} catch (err) {
console.log("file upload catch", err);
next(err);
}
});
router.delete("/api/files/:hash", async (req, res, next) => {
try {
let { hash } = req.params;
// get the file
let query = `
select * from recordDataFile where hash = ?
`;
let rows = await req.pool.query(query, [hash]);
let file = rows[0];
let filePath = file.path;
// remove file
fs.unlinkSync(filePath);
// delete the file metadata
await req.pool.query(` delete from recordDataFile where hash = ?`, [hash]);
res.status(200).send(rows);
} catch (err) {
next(err);
}
});
module.exports = router;

最新更新