Multipart Retrofit 2.0 image upload - android

I am trying to upload image by POST multipart request which should have structure like this :
-----------------------------219391268715340
Content-Disposition: form-data; name="photos[]"; filename="DSCF0157-Laptop.JPG"
Content-Type: image/jpeg
(byte-data)
My code :
MediaType mediaType = MediaType.parse("image/jpeg");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArray = stream.toByteArray();
RequestBody file=RequestBody.create(mediaType, byteArray);
map.put("form-data; name=\"photos[]\"; filename=\""+filename+".jpg",file);
I use map because of #PartMap annotation - I want to upload multiple files. My server returns http code 200 - but no files are uploaded. Api call has been tested - it works correctly if used by our web application. Any idea what I am doing wrong

If you want to upload many files in a request using Retrofit 2, you can refer to my answer at the question below
Retrofit - Multipart request: Required MultipartFile parameter 'file' is not present
With some modifications:
WebAPIService.java:
#Multipart
#POST("/api/fileupload")
Call<ResponseBody> postFiles(#Part List<MultipartBody.Part> fileList);
FileActivity.java:
...
List<MultipartBody.Part> fileList = new ArrayList<>();
for (int i = 0; i < 2; i++){
fileList.add(body);
}
Call<ResponseBody> call = service.postFiles(fileList);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call,
Response<ResponseBody> response) {
Log.i(LOG_TAG, "success");
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e(LOG_TAG, t.getMessage());
}
});
Of course, with the above code, in fact, the same file (body) will be uploaded as 2 parts in the request. As a result, in web server, you will have 2 files with the same content, you can customize the fileList with many different files :)
Hope it helps!

Maybe you can do that like this.
YourAPIService
#Multipart
#POST("api/image/upload")
Call<ImageUploadResponse> uploadImage(#Part("photos") List<RequestBody> imageFiles);
YourActivity
//prepare request body
List<RequestBody> images = new ArrayList<>();
for (int i = 0; i < filePath.size(); i++){
RequestBody file = RequestBody.create(MediaType.parse("image/*"), filePath.get(i);
images.add(file);
}
//call api
Call<ImageUploadResponse> call = imageUploadService.uploadImage(images);
call.enqueue(new Callback<ImageUploadResponse>() {
#Override
public void onResponse(Call<ImageUploadResponse> call,
Response<ImageUploadResponse> response) {
}
#Override
public void onFailure(Call<ImageUploadResponse> call, Throwable t) {
}
});

Related

Retrofit - Upload Image to S3 using presigned URL

I am getting the presigned URL from Lambda, but not sure why when I make a PUT request to that generated URL, my image is not being uploaded to S3.
I've followed these two answers exactly, but it's still not working for me
https://stackoverflow.com/a/46177449/11110509
https://stackoverflow.com/a/46579224/11110509
Could someone point out what I'm doing wrong?
public interface IUploadImages{
#PUT
Call<String> listRepos(#Url String url, #Body RequestBody image);
}
And my call to upload the image:
//Already checked to make sure mImageUri is not null
File file = new File(StringUtils.getPath(this, mImageUri));
RequestBody requestFile = RequestBody.create(MediaType.parse("image/jpeg"), file);
RetrofitInterfaces.IUploadImages service = RetrofitClientInstance.getRetrofitInstance()
.create(RetrofitInterfaces.IUploadImages.class);
//Also checked to make sure mGeneraredUrl is also not null
Call<String> call = service.listRepos(mGeneratedUrl, requestFile);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if(response.isSuccessful()){
Log.d(TAG, "onResponse: Success?");
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Log.d(TAG, "onFailure: Failed: " + t);
}
});
I've made the bucket public. The call is not failing, but in onResponse I'm getting a response code of 403.
Finally figured it out.. your presigned url function needs to have the same content type as
RequestBody.create(MediaType.parse("image/jpeg"), file);
So in my params
var params = {
Bucket: 'my-bucket-name',
Key: 'test-key',
Expires: signedUrlExpireSeconds,
ContentType: "image/jpeg"
};
Content type needs to be the same client side and in function side otherwise you get a 403 error...

Retrofit - Error 400

I'm having issues trying to upload images and other details to my server using the below code:
public static final String APP_JSON = "Content-Type: application/json";
#Multipart
#Headers({ApiClient.APP_JSON})
#POST("relatorio/{token}")
Call<JsonObject> uploadJson(#Path("token") String token, #Part("json") JsonObject auditoria, #Part List<MultipartBody.Part> files);
It always return error 400 due to SyntaxError: Unexpected token <br>;
However, if I send only the details as RequestBody it works.
I need to construct an array of images to upload as defined in my server; thus, I'm using it:
private List<MultipartBody.Part> prepare(){
Set<String> keys = JsonFormFragmentPresenter.imagesList.keySet();
List<MultipartBody.Part> parts = new ArrayList<>();
for(String k : keys){
for(String path : JsonFormFragmentPresenter.imagesList.get(k)){
if(!path.equals(null) && !path.equals("")){
File image = new File(path);
RequestBody requestFile = RequestBody.create(
MediaType.parse("multipart/form-data"), image
);
Log.e("Test", (requestFile == null) + "");
parts.add(MultipartBody.Part.createFormData("imagens", image.getName(), requestFile));
}
}
}
JsonFormFragmentPresenter.imagesList.clear();
return parts;
}
After that I make the call that is always returning 400:
Call<JsonObject> call = ApiClient.get().uploadJson(details.get(KEY_TOKEN), json, prepare());
call.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
Log.e("Status", new Gson().toJson(response));
}
#Override
public void onFailure(Call<JsonObject> call, Throwable t) {
Log.e("Status", "Faild");
Log.e("UPDATErr", new Gson().toJson(t));
}
});
Any hint on why it is not working?
Thank you,
You are using #Header annotation with Content-Type: application/json, but you need multipart POST request which uses different content type. Try to remove #Header annotation and replace it with #Multipart.

Android - Retrofit - File Uploaded to AWS S3 with pre-signed url is corrupt

In my application, I need to upload images directly to AWS S3. For this my server generates a pre-signed url and mobile client use that url to PUT file on. Though getting 200 in upload call, the file is not uploaded correctly i.e. it's corrupt and never loads back.
Following is the code being used to upload file to S3.
public static interface FileUploadService {
#PUT("/")
void upload(#Body() RequestBody body,
Callback<Object> callback);
}
ServiceGenerator.getUploadService(url).upload(
RequestBody.create(MediaType.parse("image/jpeg"), image),
new Callback<Object>() { });
I'm using Retrofit 1.8.
Hello Try this if it helps but I used Retrofit 2,
Gradle
compile 'com.squareup.retrofit2:retrofit:2.0.2'
The Interface
public static interface FileUploadService {
#Multipart
#POST(HttpConstants.FILEUPLOADJSON1)
Call<Result> uploadImage(#Part MultipartBody.Part file, #Part("stdID") int stdID);
}
The Call
RaytaApi service= RaytaServiceClass.getApiService();
File file = new File(imagePath);
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
MultipartBody.Part body =
MultipartBody.Part.createFormData("uploaded_file", file.getName(), requestFile);
Call<Result> resultCall=service.uploadImage(body,INT_STDID);
final Result[] result = {new Result()};
resultCall.enqueue(new Callback<Result>() {
#Override
public void onResponse(Call<Result> call, Response<Result> response) {
Log.v("###WWE","Respnse");
}
}
#Override
public void onFailure(Call<Result> call, Throwable t) {
Log.v("###WWE","Failure ");
Log.v("###WWE","MEssage "+t.getMessage());
}
});
For complete example referance please visit
https://github.com/pratikvyas1991/NetworkingExample-master
Fixed as following;
When using RequestBody, the Content-Length header was not carrying the correct info neither I was able to override it. So, I used TypedInput instead of RequestBody. My service interface looks like following.
public static interface FileUploadService {
#PUT("/")
void upload(#Body TypedInput body, Callback<Object> callback);
}
And image is uploaded as;
ServiceGenerator.getUploadService(url).upload(new TypedFile("image/*", file),
new Callback<Object>() { });

Unable to upload multiple files or collection of files in mutipart/form-data request using retrofit in Android

I am developing an Android application. In my app, I need to upload multiple or collection of files to server in multipart/form-data request. I am using Retrofit 2 for it. I can upload single file to server. But when I upload list of file, file values are always null at the server side.
This is my retrofit service interface
public interface ApiService {
#Multipart
#POST("Troll/CreateMemePost")
Call<ResponseBody> postMeme(#Part List<MultipartBody.Part> files, #Part("AuthToken") RequestBody authToken);
}
As you can see I am trying to upload list of file as first parameter.
This is how I upload in activity
final ArrayList<File> photoFiles = new ArrayList<File>();
ArrayList<MultipartBody.Part> files = new ArrayList<MultipartBody.Part>();
for(Bitmap bitmap: previewBitmaps)
{
File file = null;
try{
String fileName = String.valueOf(System.currentTimeMillis())+".jpeg";
file = new File(Environment.getExternalStorageDirectory(), fileName);
if(file.exists())
{
file.delete();
}
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
os.close();
photoFiles.add(file);
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"),file);
MultipartBody.Part partFile = MultipartBody.Part.createFormData("multipart/form-data",file.getName(),requestFile);
files.add(partFile);
}
catch (Exception e)
{
Toast.makeText(getBaseContext(),e.getMessage(),Toast.LENGTH_SHORT).show();
}
}
if(files!=null && files.size()>0)
{
final Retrofit retrofit = app.getApiClient().getRetrofit();
ApiService service = app.getApiClient().getApiService();
RequestBody authToken = RequestBody.create(MediaType.parse("multipart/form-data"), app.getAuthToken());
Call<ResponseBody> call = service.postMeme(files,authToken);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if(photoFiles!=null && photoFiles.size()>0)
{
for(File tempFile : photoFiles)
{
if(tempFile.exists())
{
tempFile.delete();
}
}
}
if(response!=null)
{
if(response.isSuccessful())
{
Toast.makeText(getBaseContext(),"File Uploaded successfully",Toast.LENGTH_SHORT).show();
}
else if(!response.isSuccessful() && response.errorBody()!=null)
{
Toast.makeText(getBaseContext(),"Server error message",Toast.LENGTH_SHORT).show();
/*try{
}
catch (IOException e)
{
Toast.makeText(getBaseContext(),e.getMessage(),Toast.LENGTH_SHORT).show();
}*/
}
}
else{
Toast.makeText(getBaseContext(),"Response is null",Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
if(photoFiles!=null && photoFiles.size()>0)
{
for(File tempFile : photoFiles)
{
if(tempFile.exists())
{
tempFile.delete();
}
}
}
Toast.makeText(getBaseContext(),"Unable to transfer data to server",Toast.LENGTH_SHORT).show();
}
});
}
But when I upload, I receive AuthToken field successfully at the server side. But the file values are always null. I am using ASP.NET MVC for backend. You can see the screenshot below. It is always null. U bind like this at server side.
public JsonResult CreateMemePost(IEnumerable<HttpPostedFileBase> files,CreateMemePostModel model)
files is always null and AuthToken field in model is present as sent from my app. When I upload single file like this
public JsonResult CreateMemePost(HttpPostedFileBase file,CreateMemePostModel model)
It is working and uploaded successfully. I am pretty sure that the problem is with my retrofit code. What is wrong with my code? How can I upload list of files to server in multipart/form-data request using retrofit in Android?
I tried this as well to upload files only
public interface ApiService {
#FormUrlEncoded
#POST("Troll/CreateMemePost")
Call<ResponseBody> postMeme(#Field("files") ArrayList<RequestBody> files);//, #Part("AuthToken") RequestBody authToken);
}
Files count is always 0 at the server side.
Then I tried another solution using MapPart
public interface ApiService {
#Multipart
#POST("Troll/CreateMemePost")
Call<ResponseBody> postMeme(#PartMap() Map<String,RequestBody> files, #Part("AuthToken") RequestBody authToken);
}
I create map like this in activity
HashMap<String,RequestBody> map = new HashMap<>();
// Other steps
MediaType MEDIA_TYPE=MediaType.parse("multipart/form-data");
bodyFile = RequestBody.create(MEDIA_TYPE,file);
map.put("files",bodyFile);
Files is always null at the server.
Finally, I found the solution. I used RequestBody and MultipartBody.Builder. I uploaded the solution on Github.
Please have a look at this link - https://github.com/waiyanhein/how-to-upload-multiple-files-using-retrofit-in-android.

Retrofit call back time remain download file Android

I'm implementing retrofit in my app. I use code below to getString and write to file from server.
Callback callback = new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.i(TAG, "onResponse");
File file = new File(fileDirectory);
file.mkdirs();
File outputFile = new File(file,
XXX);
try {
OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream(outputFile));
osw.write(new String(response.body().string()));
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG,"onFailure, getTermAndPoliciesDocument()");
t.printStackTrace();
}
};
PortalService portalService = ServiceGeneratorDemo.createService(PortalService.class);
Call<ResponseBody> call = portalService.getStringFromFile(AppSetting.Default.TERM_AND_POLICIES_DOC_NAME);
call.enqueue(callback);
Now i want show information about download of that file. Like 1 - 100% to complete progress like below image. . Anyone know how to callback time remain ?
Now i want show information about download of that file. Like 1 - 100% to complete progress
Your image shows time remaining, but once you know the percentage complete, you can calcuate the time yourself. Here's how I found to get the percent progress update using Retrofit 2:
First, create a progress listener:
final ProgressResponseBody.ProgressListener progressListener = new ProgressResponseBody.ProgressListener() {
#Override public void update(long bytesRead, long contentLength, boolean done) {
int percent = (int)((100 * bytesRead) / contentLength);
//Do something with the progress (I sent the event through EventBus)
}
};
Next, create a client as shown here, which will call the listener:
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.body(new ProgressResponseBody(originalResponse.body(), progressListener))
.build();
}
})
.build();
Finally, specify the client when building Reftrofit:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
It took me a while to get this figured out and I wish I could find the link that provided the final information, but this is the code I'm using. (I thought it was on Future Studio but I don't see it.)

Categories

Resources