[英]Fetching image from URL and uploading to another via POST in NodeJS
In the following snippet, I'm using node-fetch and form-data to first retrieve an image file from a remote URL and then upload it to an S3 bucket (using aws-sdk and multer in a different script):在以下代码段中,我使用node-fetch和form-data首先从远程 URL 检索图像文件,然后将其上传到 S3 存储桶(在不同的脚本中使用aws-sdk和multer ):
import fetch from 'node-fetch';
import fs from 'fs';
import FormData from 'form-data';
const form = new FormData();
const processProfileImg = (imageURL, userID) => {
fetch(imageURL, userID)
.then((response) => {
const dest = fs.createWriteStream(`./temp/${userID}.jpg`);
response.body.pipe(dest);
})
.then((dest) => {
form.append('profileImage', fs.createReadStream(`./temp/${userID}.jpg`));
fetch(`https://www.schandillia.com/upload/profile-image?userID=${userID}`, { method: 'POST', body: form })
.then(response => response.json())
.then(json => console.log(json));
});
};
export default processProfileImg;
Problem is, this involves an intermediate step of first storing the file locally upon retrieval, before it's picked up for POST by the form-data
function.问题是,这涉及一个中间步骤,即首先在检索时将文件存储在本地,然后再由
form-data
函数将其用于 POST。 Is there any way to bypass this step entirely?有没有办法完全绕过这一步? I don't want to save the file locally, I just want to pull it from the remote URL and POST it to the upload route without creating a local file.
我不想将文件保存在本地,我只想从远程 URL 中提取它并将其 POST 到上传路由,而无需创建本地文件。
Update : After slightly modifying the snippet to implement suggestions from Fransebas (first answer) and avoid async issues, I'm running into a new problem: The image being saved locally is alright, but the copy being uploaded to S3 is partially cut off!更新:在稍微修改片段以实现 Fransebas(第一个答案)的建议并避免异步问题后,我遇到了一个新问题:本地保存的图像没问题,但上传到 S3 的副本被部分切断!
Additional code : The route that handles POST upload, https://www.schandillia.com/upload/profile-image , is as follows and it works well when I try uploading the file using Postman.附加代码:处理 POST 上传的路由https://www.schandillia.com/upload/profile-image如下,当我尝试使用 Postman 上传文件时,它运行良好。
import dotenv from 'dotenv';
import express from 'express';
import aws from 'aws-sdk';
import multerS3 from 'multer-s3';
import multer from 'multer';
import path from 'path';
dotenv.config();
const router = express.Router();
// Set up S3
const s3 = new aws.S3({
accessKeyId: process.env.IAM_ACCESS_KEY_ID,
secretAccessKey: process.env.IAM_SECRET_ACCESS_KEY,
});
const checkFileType = (file, cb) => {
// Allowed ext
const filetypes = /jpeg|jpg/;
// Check ext
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
// Check mime
const mimetype = filetypes.test(file.mimetype);
if (mimetype && extname) {
return cb(null, true);
}
return cb('Error: JPEG Only!');
};
// Single Upload
const profileImgUpload = multer({
storage: multerS3({
s3,
contentType: multerS3.AUTO_CONTENT_TYPE,
bucket: `${process.env.S3_BUCKET_NAME}/w`,
acl: 'public-read',
key(req, file, cb) {
cb(null, req.query.userID + path.extname(file.originalname));
},
}),
limits: { fileSize: 2000000 }, // In bytes: 2000000 bytes = 2 MB
fileFilter(req, file, cb) {
checkFileType(file, cb);
},
}).single('profileImage');
router.post('/profile-image', (req, res) => {
profileImgUpload(req, res, (error) => {
if (error) {
console.log('errors', error);
res.json({ error });
} else if (req.file === undefined) {
// If File not found
console.log('Error: No File Selected!');
res.json('Error: No File Selected');
} else {
// If Success
const imageName = req.file.key;
const imageLocation = req.file.location;
// Save the file name into database into profile model
res.json({
image: imageName,
location: imageLocation,
});
}
});
});
// End of single profile upload
// We export the router so that the server.js file can pick it up
module.exports = router;
I haven't used that specific way of sending data (I prefer ajax) but by looking at your example I suppose you can skip saving the image locally. 我没有使用发送数据的特定方式(我更喜欢ajax),但是通过查看您的示例,我想您可以跳过本地保存图像。 If you see
fs.createReadStream
creates a read stream. 如果看到
fs.createReadStream
则会创建一个读取流。 Look for ways of creating a read stream from what you got. 寻找从您获得的内容中创建读取流的方法。
Also, I think you should put your sending code inside the then
so you don't have async problems. 另外,我觉得你应该把你送代码中的
then
所以你不必异步问题。 For example, if your code for sending data is inside the then
then you could use response.body
to create the stream. 例如,如果您的用于发送数据的代码位于内,
then
可以使用response.body
创建流。
You almost got it, but you are still using the file, I think you can archive it with something more like this 差不多了,但是您仍在使用该文件,我想您可以使用其他类似方式将其归档
import fetch from 'node-fetch';
import fs from 'fs';
import FormData from 'form-data';
const form = new FormData();
const processProfileImg = (imageURL, userID) => {
fetch(imageURL, userID)
.then((response) => {
// Use response.body directly, it contains the image right?
form.append('profileImage', response.body);
fetch(`https://www.schandillia.com/upload/profile-image?userID=${userID}`, { method: 'POST', body: form })
.then(response => response.json())
.then(json => console.log(json));
});
};
export default processProfileImg;
If I understand the documentation of fetch
correctly response.body
is already a stream. 如果我正确理解
fetch
文档,则response.body
已经是一个流。
This works for me:这对我有用:
const axios = require('axios')
const FormData = require('form-data');
//Get image
let imageResponse = await axios({
url: imageUrl,
method: 'GET',
responseType: 'arraybuffer'
})
//Create form data
const form = new FormData()
form.append('image', imageResponse.data, {
contentType: 'image/jpeg',
name: 'image',
filename: 'imageFileName.jpg'
})
//Submit form
let result = await axios({
url: serverUrl,
method: "POST",
data: form,
headers: { "Content-Type": `multipart/form-data; boundary=${form._boundary}` }
})
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.