Estoy tratando de pasar una imagen cargada desde una aplicación de reacción a través de express a un depósito s3 administrado. La plataforma/host que estoy usando crea y administra el depósito s3 y genera direcciones URL de carga y acceso. Todo esto funciona bien (he probado una URL de carga generada en cartero con una imagen en un cuerpo binario y funcionó perfectamente).
Mi problema es pasar la imagen a través de express. Estoy usando multer para obtener la imagen del formulario, pero asumo que multer está convirtiendo esa imagen en algún tipo de objeto de archivo y s3 espera algún tipo de blob o flujo.
En el siguiente código, la imagen en el archivo req. existe, recibo una respuesta 200 de s3 sin errores y cuando visito la URL del activo, la URL funciona, pero falta la imagen.
const router = Router(); const upload = multer() router.post('/', upload.single('file'), async (req, res) => { console.log(req.file) const asset = req.file const assetPath = req.headers['asset-path'] let s3URLs = await getPresignedURLS(assetPath) const sendAsset = await fetch( s3URLs.urls[0].upload_url, // the s3 upload url { method: 'PUT', headers: { "Content-Type": asset.mimetype }, body: asset, redirect: 'follow' } ) console.log("s3 response", sendAsset) res.status(200).json({"url": s3URLs.urls[0].access_url }); }); export default router;No estoy seguro de qué hacer para convertir lo que multer me da en algo que aws s3 acepte. También estoy abierto a deshacerme de multer si hay una manera más fácil de cargar archivos binarios para expresar.
En lugar de multer, puede usar multiparte para obtener datos de archivo del objeto de solicitud. Y para cargar el archivo en el depósito s3, puede usar aws-sdk .
const AWS = require("aws-sdk"); const multiparty = require("multiparty"); /** * Helper method which takes the request object and returns a promise with a data. */ const getDataFromRequest = (req) => new Promise(async(resolve, reject) => { const form = new multiparty.Form(); await form.parse(req, (err, fields, files) => { if (err) reject(err); const bucketname = fields.bucketname[0]; const subfoldername = fields.subfoldername[0]; const file = files["file"][0]; // get the file from the returned files object if (!file) reject("File was not found in form data."); else resolve({ file, bucketname, subfoldername }); }); }); /** * Helper method which takes the request object and returns a promise with the AWS S3 object details. */ const uploadFileToS3Bucket = ( file, bucketname, subfoldername, options = {} ) => { const s3 = new AWS.S3(); // turn the file into a buffer for uploading const buffer = readFileSync(file.path); var originalname = file.originalFilename; var attach_split = originalname.split("."); var name = attach_split[0]; // generate a new random file name const fileName = name; // the extension of your file const extension = extname(file.path); console.log(`${fileName}${extension}`); const params = { Bucket: bucketname, //Bucketname ACL: "private", //Permission Key: join(`${subfoldername}/`, `${fileName}${extension}`), // File name you want to save as in S3 Body: buffer, // Content of file }; // return a promise return new Promise((resolve, reject) => { return s3.upload(params, (err, result) => { if (err) reject(err); else resolve(result); // return the values of the successful AWS S3 request }); }); }; router.post('/', upload.single('file'), async(req, res) => { try { // extract the file from the request object const { file, bucketname, subfoldername } = await getDataFromRequest(req); // Upload File to specified bucket const { Location, ETag, Bucket, Key } = await uploadFileToS3Bucket( file, bucketname, subfoldername ); let response = {}; res["Location"] = Location; response["ETag"] = ETag; response["Bucket"] = Bucket; response["Key"] = Key; res.status(200).json(response); } catch (error) { throw error; } });El cuerpo de la solicitud será un formulario de datos con los siguientes campos
Para cualquiera que alguna vez se encuentre con esta pregunta, la solución fue crear un motor de almacenamiento personalizado . Dentro del motor, obtiene acceso al archivo con una propiedad de flujo que s3 aceptó (con los encabezados correctos).