es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Flutter/Dart carga un archivo zip multipartida prefirmado en S3 y añade un encabezado adicional, lo cual provoca que el archivo zip no se pueda descomprimir.

Estoy tratando de cargar un archivo zip a AWS S3 con una URL prefirmada.
Obtengo la URL prefirmada que parece funcionar muy bien con Postman. Con Flutter también funciona, hasta cierto punto.

Sin embargo, el problema es que al cargar a través de Flutter (utilicé este fragmento), cargará el archivo zip, pero agregará 184 bytes adicionales al encabezado que contiene lo siguiente:

–dart-http-boundary-z86XWhmTq935Bbfwfv.7UA+FIOU5nE4eqfUsZTh6PnGj+HBeAYH
content-type: application/octet-stream
content-disposition: form-data; name=”out.zip”; filename=”out.zip”

Este encabezado hace que la mayoría de las herramientas integradas de descompresión no puedan descomprimir el archivo.

Pude descomprimirlo usando el comando unzip integrado de osx, pero al descargarlo a través de Safari, por ejemplo, obtengo el mensaje “Fallo en la descompresión” en la sección de descargas.

En Windows no pude descomprimirlo en absoluto, incluso con 7-Zip.

¿Hay alguna manera de eliminar el encabezado para que cargue solo el archivo zip tal como está sin agregar encabezados adicionales?

Aviso legal: No soy un desarrollador Flutter/Dart por naturaleza.

¡Gracias!

EDIT 1:
Para crear la URL firmada (siguiendo la documentación de AWS), uso la siguiente función lambda:

'use strict'

const AWS = require('aws-sdk');
AWS.config.update({ region: process.env.AWS_REGION });
const s3 = new AWS.S3();

const URL_EXPIRATION_SECONDS = 300;

exports.handler = async function(event, context, callback) {
  const body = event.body || {};
  return await getUploadURL(JSON.parse(event.body).projectNumber)
}

const getUploadURL = async function(projectNumber) {
  let Key = `${projectNumber}.zip`;

  if (!projectNumber) {
    const randomID = parseInt(Math.random() * 10000000)
    Key = `${randomID}.zip`;
  }

  // Obtener la URL firmada de S3
  const s3Params = {
    Bucket: process.env.UploadBucket,
    Key,
    Expires: URL_EXPIRATION_SECONDS,
    ContentType: 'application/zip',
    // Este ACL hace que el objeto cargado sea de lectura pública. También debe descomentar
    // el permiso adicional para la función Lambda en la plantilla SAM.
    // ACL: 'public-read'
  }

  console.log('Params: ', s3Params)
  const uploadURL = await s3.getSignedUrlPromise('putObject', s3Params)

  return JSON.stringify({
    uploadURL: uploadURL,
    Key
  })
}
Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Parece que el problema que estás enfrentando está relacionado con los encabezados content-type y content-disposition que son automáticamente agregados por la biblioteca http de Flutter al enviar la solicitud multipartes. Estos encabezados son necesarios para que el servidor interprete correctamente la solicitud y extraiga los datos del archivo del mensaje multipartes, pero pueden causar problemas al intentar descomprimir el archivo cargado.

    Para solucionar este problema, puedes intentar modificar la solicitud http para excluir los encabezados content-type y content-disposition utilizando el método http.MultipartFile.fromBytes en lugar de http.MultipartFile.fromPath. Este método te permite especificar los datos del archivo como bytes sin encabezados adicionales.

    Aquí tienes un ejemplo de cómo puedes modificar el fragmento de código que proporcionaste para usar http.MultipartFile.fromBytes:

    Future<void> uploadFile(File file, String url) async {
      List<int> fileBytes = await file.readAsBytes();
      String filename = basename(file.path);
      String contentType = 'application/zip'; // reemplaza con el tipo MIME correcto si es necesario
      http.MultipartFile fileData = http.MultipartFile.fromBytes('file', fileBytes, filename: filename, contentType: MediaType.parse(contentType));
      http.StreamedRequest request = http.StreamedRequest('POST', Uri.parse(url));
      request.headers['Content-Type'] = 'multipart/form-data; boundary=${httpRequest.boundary}';
      request.headers['x-amz-server-side-encryption'] = 'AES256'; // si es necesario
      request.headers['x-amz-acl'] = 'private'; // si es necesario
      request.headers['x-amz-storage-class'] = 'STANDARD'; // si es necesario
      request.headers['x-amz-tagging'] = 'key1=value1'; // si es necesario
      request.headers['user-agent'] = 'curl/7.64.1'; // si es necesario
      request.headers['accept'] = '*/*'; // si es necesario
      request.headers['host'] = 's3.YOUR_DOMAIN.com'; // reemplaza con la URL de tu depósito de S3
      List<int> bodyBytes = request.finalize().toBytes();
      Uint8List requestBody = Uint8List.fromList(bodyBytes);
      request.sink.add(requestBody);
      request.sink.add(fileData.toList());
      request.sink.add(httpRequest.close());
      http.StreamedResponse response = await request.send();
      if (response.statusCode != 200) {
        throw Exception('No se pudo cargar el archivo');
      }
    }
    

    Esto debería permitirte cargar el archivo en S3 sin encabezados adicionales, lo que debería evitar problemas al descomprimir el archivo.

Comments are closed.