Vydia / react-native-background-upload

Upload files in your React Native app even while it's backgrounded. Supports Android and iOS, including camera roll assets.
MIT License
730 stars 333 forks source link

Failed to upload file directly to S3 #302

Open thangnt-savvy opened 2 years ago

thangnt-savvy commented 2 years ago

I tried to upload a file directly to S3, but my order of parameters is not as required. Seem I can't change the order of my request.

Here is my code

  const options: any = {
    url: url,
    path: path,
    method: 'POST',
    type: 'multipart',
    field: 'file',
    maxRetries: 2, // set retry count (Android only). Default 2
    headers: {
      'content-type': fileInfo.mimeType, // server requires a content-type header
      'content-length': `${fileInfo.size}`,
    },
    parameters: {
      key: `${fields.key}/${fileInfo.name}`,
      acl: fields['acl'],
      bucket: fields['bucket'],
      'X-Amz-Algorithm': fields['X-Amz-Algorithm'],
      'X-Amz-Credential': fields['X-Amz-Credential'],
      'X-Amz-Date': fields['X-Amz-Date'],
      'X-Amz-Signature': fields['X-Amz-Signature'],
      Policy: fields['Policy'],
      'Content-Type': fileInfo.mimeType,
    },
    notification: {
      enabled: true,
    },
    useUtf8Charset: true,
  };

  Upload.startUpload(options)
    .then(uploadId => {
      console.log('Upload started');
      Upload.addListener('progress', uploadId, data => {
        console.log(`Progress: ${data.progress}%`);
      });
      Upload.addListener('error', uploadId, data => {
        console.log(`Error: ${data.error}%`);
      });
      Upload.addListener('cancelled', uploadId, data => {
        console.log('Cancelled!', data);
      });
      Upload.addListener('completed', uploadId, data => {
        // data includes responseCode: number and responseBody: Object
        console.log('Completed!', data);
      });
    })
    .catch(err => {
      console.log('Upload error!', err);
    });

and S3 error message Key should be the first item instead of file

<?xml version="1.0" encoding="UTF-8"?>
<Error>
  <Code>InvalidArgument</Code>
  <Message>Bucket POST must contain a field named 'key'.  If it is specified, please check the order of the fields.</Message>
  <ArgumentName>key</ArgumentName>
  <ArgumentValue></ArgumentValue>
</Error>
anyb1s commented 2 years ago

Hi there, we got to work with pre-signed URLs. The change needed is to send it with PUT and type 'raw'.

nodabasi commented 2 years ago

@anyb1s right I also using pre-signed URL with some configurations but it only seems like work in simulators any ideas why is not working on the real devices

frank137 commented 2 years ago

Hi there, we got to work with pre-signed URLs. The change needed is to send it with PUT and type 'raw'.

Hi, I am having the same problem. @anyb1s , if I use type 'raw' I get an error that I cannot pass extra parameters: Error: Parameters supported only in multipart type

How did you make it work on your side? I need to send the parameters that contain the amazon policies and so on to make the pre-signed URL work. Thanks!

anyb1s commented 2 years ago

What we do is the following we have an endpoint to presign `$command = $adapter->getClient()->getCommand('putObject', array_filter([ 'Bucket' => $adapter->getBucket(), 'Key' => $path, 'ACL' => 'public-read', 'ContentType' => $request->get('type'), 'Metadata' => [ 'content-type' => $request->get('type'), ] ]));

    $signedRequest = $adapter->getClient()->createPresignedRequest($command, '+5 minutes');

    return response()->json([
        'method' => $signedRequest->getMethod(),
        'url' => (string)$signedRequest->getUri(),
        'fields' => [],
        'path' => 'tmp/' . $filename,
        'headers' => [
            'content-type' => $request->get('type'),
        ],
    ]);`

const options = { url: presignResponse.url, path: imagePath, method: presignResponse.method, type: 'raw', field: 'file', maxRetries: 2, // set retry count (Android only). Default 2 headers: { 'content-type': mimeType, // Customize content-type accept: 'application/json', // Customize content-type }, // Below are options only supported on Android notification: { enabled: true, autoclear: true, onProgressTitle: 'Uploading...', onCompleteTitle: 'Upload finished', }, useUtf8Charset: true, };