我在使用 node.js 编写的应用程序在 docker 容器内运行时遇到了一些奇怪的行为。
它使用 react-admin ( https://marmelab.com/react-admin/ ) 前端成功上传和删除图像。
-rw-r--r-- 1 root root 87.2K Feb 13 2021 60283f6b7b33b304a4b6b428.webp
-rw-r--r-- 1 root root 32.2K Dec 7 01:54 60283f6b7b33b304a4b6b428_1.jpg
如果扩展名不同,它甚至会用新的图像替换以前上传的图像。
-rw-r--r-- 1 root root 87.2K Feb 13 2021 60283f6b7b33b304a4b6b428.webp
-rw-r--r-- 1 root root 674.1K Dec 7 02:26 60283f6b7b33b304a4b6b428_1.png
但是由于某种原因,如果我上传的图片与之前上传的完全不同,但扩展名相同,导致图片与第一个删除的图片同名,那么将显示该图片,而不是较新的。
-rw-r--r-- 1 root root 87.2K Feb 13 2021 60283f6b7b33b304a4b6b428.webp
-rw-r--r-- 1 root root 32.2K Dec 7 01:54 60283f6b7b33b304a4b6b428_1.jpg
上述应用程序使用 multer 作为中间件上传文件:
const multer = require("multer");
const path = require("path");
const {
PATH_PRODUCT_TEMP_IMAGE
} = require("../services/config");
var storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, PATH_PRODUCT_TEMP_IMAGE),
filename: (req, file, cb) => {
if (file.fieldname === "picture")
cb(null, `${req.params.id}${path.extname(file.originalname)}`);
else if (file.fieldname === "picture1")
cb(null, `${req.params.id}_1${path.extname(file.originalname)}`);
},
});
// Filter files with multer
const multerFilter = (req, file, cb) => {
if (file.mimetype.startsWith("image")) {
cb(null, true);
} else {
cb("Not an image! Please upload only images.", false);
}
};
const maxFileUploadSizeMb = 15;
var upload = multer({
storage: storage,
limits: { fileSize: maxFileUploadSizeMb * 1024 * 1024 },
fileFilter: multerFilter,
});
module.exports = upload;
管理文件上传的 API 控制器是:
router.put(
"/:id",
[
auth,
upload.fields([
{ name: "picture", maxCount: 1 },
{ name: "picture1", maxCount: 1 },
]),
],
async (req, res) => {
try {
let objToUpdate = buildProduct(req.body);
const product = await Product.findById(req.params.id);
if (!product) throw new Error(`Product ${req.params.id} doesn't exist`);
if (req.files.picture?.length > 0) {
objToUpdate = {
...objToUpdate,
primaryImage: req.files.picture[0].filename,
};
removeProductImage(product.primaryImage);
resizeProductImage(objToUpdate.primaryImage);
}
if (req.files.picture1?.length > 0) {
objToUpdate = {
...objToUpdate,
image1: req.files.picture1[0].filename,
};
removeProductImage(product?.image1);
resizeProductImage(objToUpdate.image1);
}
await product.updateOne(objToUpdate);
res.send(product);
} catch (error) {
sendError500(res, error);
}
}
);
const removeProductImage = async (imageName) => {
if (notNullOrEmpty(imageName))
return await removeFile(path.join(PATH_PRODUCT_IMAGE, imageName));
};
const removeFile = async (filePathName) => {
let result = false;
try {
await fs.unlink(filePathName, (error) => {
if (error) throwError(error);
else result = true;
});
} catch (error) {
throwError(error);
}
return result;
function throwError(error) {
throw new Error(`Can't delete file: ${filePathName} - ${error.message}`);
}
};
整个项目在 docker 中运行,使用命名卷作为图像的存储。尽管如此,使用相同的代码库但使用绑定安装,它可以按预期工作。
编辑:我注意到我忘记发布该过程中涉及的函数:
const resizeProductImage = async (imageName) => {
if (!notNullOrEmpty(imageName)) return;
const imageTemp = path.join(PATH_PRODUCT_TEMP_IMAGE, imageName);
const imageFinal = path.join(PATH_PRODUCT_IMAGE, imageName);
await resizeImage({
imageFinal,
imageTemp,
imageFinalSize: +IMAGE_SIZE_PRODUCT,
});
await removeProductTempImage(imageName);
};
const resizeImage = async ({
imageFinal,
imageTemp,
imageFinalSize = 1024,
}) => {
try {
switch (path.extname(imageTemp)) {
case ".png":
await sharp(imageTemp)
.resize(imageFinalSize, null)
.png({ adaptiveFiltering: true })
.toFile(imageFinal, null);
break;
case ".webp":
case ".jpg":
case ".jpeg":
default:
await sharp(imageTemp)
.resize(imageFinalSize, null)
.toFile(imageFinal, null);
break;
}
} catch (error) {
try {
await fs.rename(imageTemp, imageFinal);
throw Error(
`Can't resize image ${imageTemp}, moving directly to ${imageFinal}, ${error.message}`
);
} catch (error) {
throw Error(
`Can't resize image ${imageTemp}, neither move to ${imageFinal}, ${error.message}`
);
}
}
};