Estoy tratando de cargar un archivo a S3 directamente desde el navegador. Para esto, estoy generando una URL de carga firmada en mi servidor.
Cuando intento cargar un archivo a la url generada desde el navegador usando
var xhr = new XMLHttpRequest(); xhr.open('PUT', signedUrl); xhr.send(file);
Obtuve el siguiente error
<Error> <Code> SignatureDoesNotMatch </Code> <Message> The request signature we calculated does not match the signature you provided. Check your key and signing method. </Message> ... </Error>
Puedo subir a la URL firmada usando curl con el siguiente comando
curl -T <file> -X PUT '<signed-url>'
El navegador está enviando mi solicitud como datos de formulario de varias partes, lo que está causando el problema. ¿Cómo soluciono el problema?
Estoy generando la URL firmada usando el siguiente código
var params = { Bucket: bucket, Key: s3Key, ACL: "authenticated-read", Expires: 600 }; S3.getSignedUrl('putObject', params, (err, data) => { const returnData = { url: data }; reply(returnData); });
Editar: Para que quede claro, he permitido que todos los orígenes envíen solicitudes PUT en la configuración CORS del depósito.
De acuerdo, el problema era que el navegador enviaba Content-Type en el encabezado de la solicitud, pero la URL firmada no se generaba para el tipo de contenido. En lugar de esto
var params = { Bucket: bucket, Key: s3Key, ACL: "authenticated-read", Expires: 600 }; S3.getSignedUrl('putObject', params, ...);
hice esto
var params = { Bucket: bucket, Key: s3Key, ACL: "authenticated-read", Expires: 600, ContentType: require('mime').lookup(filename) }; S3.getSignedUrl('putObject', params, ...);
¡Y funcionó!