简体   繁体   中英

Problems uploading file to Amazon S3 using Retrofit2 on Android

I am trying to upload a file (taken by my device's camera) to Amazon S3. Unfortunately, the request fails with an InvalidArgument exception:

<Code>InvalidArgument</Code><Message></Message><ArgumentName>x-amz-acl</ArgumentName>

The request should take the form of:

------WebKitFormBoundaryPJf5u1BglONosgFh
Content-Disposition: form-data; name="key"

uploads/FooBar/<SOME ID>/db8218d9-2e45-4ed4-bd44-70019bbf6047_${filename}
------WebKitFormBoundaryPJf5u1BglONosgFh
Content-Disposition: form-data; name="success_action_status"

201
------WebKitFormBoundaryPJf5u1BglONosgFh
Content-Disposition: form-data; name="acl"

public-read
------WebKitFormBoundaryPJf5u1BglONosgFh
Content-Disposition: form-data; name="Expires"

Wed, 11 May 2016 13:26:28 GMT
------WebKitFormBoundaryPJf5u1BglONosgFh
Content-Disposition: form-data; name="file"; filename="blob"
Content-Type: image/png


------WebKitFormBoundaryPJf5u1BglONosgFh--

I have the Retrofit logger enabled, however unfortunately it does not output the request body.

I do not set x-amz-acl in my header.

My service is as follows:

 public interface FileUploadService {
      @Multipart
      @POST("/")
      Call<FileUpload> upload(@PartMap LinkedHashMap<String,String> params, @Part MultipartBody.Part file);
 }

Inside my camera fragment, I do:

// Get my image
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);

RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), bytes);
MultipartBody.Part body = MultipartBody.Part.createFormData("file","filename.jpg", requestFile);

// Get parameters from upload URL request
LinkedHashMap<String, String> params = response.body().getFields();


uploadService.upload(params, body).enqueue(new Callback<FileUpload>() {

         @Override
         public void onResponse(Call<FileUpload> call, Response<FileUpload> response) {
              // DO STUFF
         }

         @Override
         public void onFailure(Call<FileUpload> call, Throwable t) {
             // DO STUFF
         }
});

The parameters from the URL request are all correct.

If you don't mind an alternative approach to your problem, please let me suggest the following approach.

  • Download commons-net-3.4.jar from apache commons website
  • After unzipping the downloaded file, add the jar file to your libs/ folder.
  • Then Right-Click -> Add as Library to your app module. Should be straightforward. You will notice it will add the library to your build.gradle file and ready for use.
  • When you are ready, regardless of which approach you want to do the background operation of uploading the file, you can use the following sample code:

     public class SyncFileToServer extends AsyncTask<String, Void, String>{ @Override protected String doInBackground(String... params){ FTPClient ftpClient = new FTPClient(); try{ ftpClient.connect("xxx.xxx.x.xxx"); ftpClient.login("username", "password"); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); ftpClient.enterLocalPassiveMode(); File file = new File("path/to/file"); FileInputStream inputStream = new FileInputStream(file); if(ftpClient.storeFile("filetotransfer", inputStream)){ ftpClient.disconnect(); return "success"; }else{ ftpClient.disconnect(); return "ERROR"; } }catch (IOException e){ e.printStackTrace(); } } return null; } 

Since you have to get the file location from your device, you can simply pass it as a param to the async task or even a service. When the operation is completed, you can update the UI accordingly using events or something you like.

Using the commons library is much easier and faster in my opinion because you only need to write a few lines of code!

I hope this helps you solve your issue and good luck!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM