完全控制在 nodejs 中使用 ajax 发送的文件
Full control over files sent with ajax in nodejs
我使用 multer
来解析作为 multipart/data-form
和 axios
发送的多个文件
...
const storage = multer.diskStorage({
destination: './gallery',
filename(req, file, cb) {
(1) ....
},
});
const upload = multer({ storage });
router.post('/products', upload.array('images'), (req, res, next) => {
Product.create(...)
.then((product) => {
(2) ...
})
.catch(..)
})
...
此时一切正常,我的图像已保存。
问题是我想在 (1) 或 (2) 中循环并像这样命名我的文件
files.forEach((file, index) => {
// rename file to => product_id + '_' + index + '.jpeg'
}
例如,如果我有 3 个文件,它们将被命名为
5a9e881c3ebb4e1bd8911126_1.jpeg
5a9e881c3ebb4e1bd8911126_2.jpeg
5a9e881c3ebb4e1bd8911126_3.jpeg
其中5a9e881c3ebb4e1bd8911126
是mongoose
保存的产品文档的id。
- 如何解决这个命名问题?
- 是
multer
最好的解决方案,因为我想完全控制我的文件吗?
- 是否有更好的方法使用另一个节点包?
- 以
multipart/data-form
或data URL base64
发送图像好吗?
您不能只在 (1)
中设置名称,因为那时您还不知道产品的 ID。
您不能仅在 (2)
中设置名称,因为此时文件已经保存(文件名由您的 filename(req, file, cb)
函数生成)。
所以我认为最好的解决方案可能是在文件上传后移动。
这可以在 (2)
中完成。当您在路由器中处理文件时,req.files
将是一个已经上传的文件数组。
在 Product.create
的承诺回调中,您可以访问产品(ID 需要)和文件列表(编号需要)。
为此,您可以使用 fs.rename(oldPath, newPath, callback)
。
https://nodejs.org/docs/latest/api/fs.html#fs_fs_rename_oldpath_newpath_callback
像这样的东西应该可以工作:
Product.create(...).then((product) => {
req.files.forEach((file, index) => {
// file.path is the full path to the file that was uploaded.
// newPath is where you want to put it.
// Let's use the same destination and just change the filename.
const newPath = file.destination + product.id + '_' + index
fs.rename(file.path, newPath)
})
})
这很容易,只要您了解 express 的工作原理即可。因此,在开始解决问题之前,有一个清晰的认识很重要。
当您有如下快递代码时
router.post('/abc', function(req, res) {res.send('hello world');})
Express 传递来自 middlewares/functions 链的请求。现在每个函数都有 req, res, next
个参数。 next
是一个函数,中间件应该在处理完成时调用它。如果中间件决定不调用 next
,则请求到此结束,不再调用更多中间件。
当我们使用function(req, res) {res.send('hello world');}
时,我们根本没有带next
参数,这意味着我们对任何其他代码做任何事情都不感兴趣。现在回到我们的问题
router.post('/products', upload.array('images'), (req, res, next) => {...}
您首先使用了 upload.array('images')
,然后使用了您的实际产品 creation
代码。所以我会展示两种解决这个问题的方法
多一个重命名文件的中间件
router.post('/products', upload.array('images'), (req, res, next) => {
Product.create(...)
.then((product) => {
req.product = product
next();
})
.catch(..)
}, (req, res, next) => {
//Get the product id using req.product
//Move the files as per the name you desire
})
反转处理顺序
在这种方法中,您首先创建产品,然后进行图像处理。我已经创建了一个示例来显示相同的
let express = require('express');
let bodyParser = require('body-parser');
app = express();
let multer = require('multer');
const storage = multer.diskStorage({
destination: './gallery',
filename: (req, file, cb) => {
console.log('Product id - ' + req.product_id);
cb(null, req.product_id + '.js');
},
});
const upload = multer({ storage });
app.all('/', (req, res, next) => {
console.log('Hello you');
promise = new Promise((resolve) => {
// simulate a async product creation
setTimeout(() => resolve(1234), 100);
});
promise.then((product_id) => {
console.log('create the product and get the new product id')
// set the product id in the request object, so the multer
// filename function can access it
req.product_id = product_id;
res.send('uploaded files');
if (next)
next();
});
}, upload.array('images'));
module.exports = {
app
};
app.listen(8020);
并且使用 postman 测试它工作正常
编辑:2018 年 3 月 19 日
对于多个文件,您可以像下面这样轻松更新文件名函数代码
const storage = multer.diskStorage({
destination: './gallery',
filename: (req, file, cb) => {
req.file_id = req.file_id || 0;
req.file_id++;
console.log('Product id - ' + req.product_id);
cb(null, req.product_id +'_' + req.file_id +'.js');
},
});
这将确保您获得该产品的所有文件。现在回答你的问题
how to solve this naming issue ?
这个答案已经做到了
is multer the best solution cause i want full control over my files ?
不好说,只要能用,做你想做的,就够了
Is there a better approach with another node package ?
我找不到很多包裹。但是如果你想
,你可以探索this
is it good to send images as multipart/data-form or data URL base64 ?
我会使用multipart/data-form
,这样客户端就不需要进行base64转换了。但这也是见仁见智的问题。
我使用 multer
来解析作为 multipart/data-form
和 axios
...
const storage = multer.diskStorage({
destination: './gallery',
filename(req, file, cb) {
(1) ....
},
});
const upload = multer({ storage });
router.post('/products', upload.array('images'), (req, res, next) => {
Product.create(...)
.then((product) => {
(2) ...
})
.catch(..)
})
...
此时一切正常,我的图像已保存。
问题是我想在 (1) 或 (2) 中循环并像这样命名我的文件
files.forEach((file, index) => {
// rename file to => product_id + '_' + index + '.jpeg'
}
例如,如果我有 3 个文件,它们将被命名为
5a9e881c3ebb4e1bd8911126_1.jpeg
5a9e881c3ebb4e1bd8911126_2.jpeg
5a9e881c3ebb4e1bd8911126_3.jpeg
其中5a9e881c3ebb4e1bd8911126
是mongoose
保存的产品文档的id。
- 如何解决这个命名问题?
- 是
multer
最好的解决方案,因为我想完全控制我的文件吗? - 是否有更好的方法使用另一个节点包?
- 以
multipart/data-form
或data URL base64
发送图像好吗?
您不能只在 (1)
中设置名称,因为那时您还不知道产品的 ID。
您不能仅在 (2)
中设置名称,因为此时文件已经保存(文件名由您的 filename(req, file, cb)
函数生成)。
所以我认为最好的解决方案可能是在文件上传后移动。
这可以在 (2)
中完成。当您在路由器中处理文件时,req.files
将是一个已经上传的文件数组。
在 Product.create
的承诺回调中,您可以访问产品(ID 需要)和文件列表(编号需要)。
为此,您可以使用 fs.rename(oldPath, newPath, callback)
。
https://nodejs.org/docs/latest/api/fs.html#fs_fs_rename_oldpath_newpath_callback
像这样的东西应该可以工作:
Product.create(...).then((product) => {
req.files.forEach((file, index) => {
// file.path is the full path to the file that was uploaded.
// newPath is where you want to put it.
// Let's use the same destination and just change the filename.
const newPath = file.destination + product.id + '_' + index
fs.rename(file.path, newPath)
})
})
这很容易,只要您了解 express 的工作原理即可。因此,在开始解决问题之前,有一个清晰的认识很重要。
当您有如下快递代码时
router.post('/abc', function(req, res) {res.send('hello world');})
Express 传递来自 middlewares/functions 链的请求。现在每个函数都有 req, res, next
个参数。 next
是一个函数,中间件应该在处理完成时调用它。如果中间件决定不调用 next
,则请求到此结束,不再调用更多中间件。
当我们使用function(req, res) {res.send('hello world');}
时,我们根本没有带next
参数,这意味着我们对任何其他代码做任何事情都不感兴趣。现在回到我们的问题
router.post('/products', upload.array('images'), (req, res, next) => {...}
您首先使用了 upload.array('images')
,然后使用了您的实际产品 creation
代码。所以我会展示两种解决这个问题的方法
多一个重命名文件的中间件
router.post('/products', upload.array('images'), (req, res, next) => {
Product.create(...)
.then((product) => {
req.product = product
next();
})
.catch(..)
}, (req, res, next) => {
//Get the product id using req.product
//Move the files as per the name you desire
})
反转处理顺序
在这种方法中,您首先创建产品,然后进行图像处理。我已经创建了一个示例来显示相同的
let express = require('express');
let bodyParser = require('body-parser');
app = express();
let multer = require('multer');
const storage = multer.diskStorage({
destination: './gallery',
filename: (req, file, cb) => {
console.log('Product id - ' + req.product_id);
cb(null, req.product_id + '.js');
},
});
const upload = multer({ storage });
app.all('/', (req, res, next) => {
console.log('Hello you');
promise = new Promise((resolve) => {
// simulate a async product creation
setTimeout(() => resolve(1234), 100);
});
promise.then((product_id) => {
console.log('create the product and get the new product id')
// set the product id in the request object, so the multer
// filename function can access it
req.product_id = product_id;
res.send('uploaded files');
if (next)
next();
});
}, upload.array('images'));
module.exports = {
app
};
app.listen(8020);
并且使用 postman 测试它工作正常
编辑:2018 年 3 月 19 日
对于多个文件,您可以像下面这样轻松更新文件名函数代码
const storage = multer.diskStorage({
destination: './gallery',
filename: (req, file, cb) => {
req.file_id = req.file_id || 0;
req.file_id++;
console.log('Product id - ' + req.product_id);
cb(null, req.product_id +'_' + req.file_id +'.js');
},
});
这将确保您获得该产品的所有文件。现在回答你的问题
how to solve this naming issue ?
这个答案已经做到了
is multer the best solution cause i want full control over my files ?
不好说,只要能用,做你想做的,就够了
Is there a better approach with another node package ?
我找不到很多包裹。但是如果你想
,你可以探索thisis it good to send images as multipart/data-form or data URL base64 ?
我会使用multipart/data-form
,这样客户端就不需要进行base64转换了。但这也是见仁见智的问题。