OkHTTPClient hangs on Multipart request - android

I'm running OkHTTP3 on Android Lollipop and I have the following static method to upload an image in a multipart form request:
public static void uploadImage(Context context, String imageTitle, Uri imageUri, CallbackReceiver callbackReceiver) {
ContentResolver contentResolver = context.getContentResolver();
MimeTypeMap mime = MimeTypeMap.getSingleton();
String type = mime.getExtensionFromMimeType(contentResolver.getType(imageUri));
String filePath = imageUri.toString();
String fileName = imageUri.getLastPathSegment();
File file = new File(filePath);
MediaType mediaType = MediaType.parse(type);
RequestBody rb = RequestBody.create(mediaType, file);
requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("image", fileName, rb)
.build();
Request request = new Request.Builder()
.url(RequestConstants.POST_IMAGE)
.addHeader(
RequestConstants.HTTP_HEADER_AUTHORIZATION,
RequestConstants.HTTP_AUTHORIZATION_PREFIX +
AccessToken.get())
.addHeader(
RequestConstants.HTTP_HEADER_ACCEPT,
RequestConstants.HTTP_APPLICATION_JSON)
.post(requestBody)
.build();
run(request, callbackReceiver);
The RequestConstants are String values that work in other GET and POST requests.
The run method implementation:
private static OkHttpClient client = new OkHttpClient();
private static void run(Request request, final CallbackReceiver callbackReceiver) {
try {
client.newCall(request).enqueue(new Callback() {
#Override
public void onResponse(Call call, Response response) throws IOException {
callbackReceiver.onSuccess(response);
}
#Override
public void onFailure(Call call, IOException e) {
callbackReceiver.onFailure(e);
}
});
} catch (Exception e) {
Log.e("OkHttpClient", e.getMessage());
}
}
Whenever I run the request, I never receive an answer - neither the onSuccess nor the onFailure callbacks are executed.
In Logcat I can see that there are several 'suspending all threads', which could indicate a performance issue, although I am testing with a very small image (~250kB).
Any idea what could be causing this?

I solved this a while ago. To make sure it's not left unanswered: I increased the timeout on the client. I was unable to find the piece of code that fixed it.

Related

Android upload gif file to .net web API got an internal server error

I tested .net web API with postman is working but I try to use okhttp3 and retrofit2 in android. Both of them I got internal server error 500. What wrong it is? What should I do?
Gradle
implementation 'com.squareup.okhttp3:okhttp:3.4.1'
implementation 'com.squareup.okhttp3:logging-interceptor:3.4.1'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
Android Retrofit (When click button call uploadFile method)
public interface UploadAPIs {
#Multipart
#POST("FileUpload")
Call<ResponseBody> uploadFile(
#Part ("description") RequestBody description,
#Part MultipartBody.Part file);
}
public Boolean uploadFile(File file) {
RequestBody descriptionPart = RequestBody.create(MultipartBody.FORM, file.getName());
RequestBody filePart = RequestBody.create(MediaType.parse("image/gif"), file);
MultipartBody.Part fileMulti = MultipartBody.Part.createFormData("file"
, file.getName(), filePart);
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("http://xxxxxxxxxx/api/FileExplorerApi/")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
UploadAPIs uploadAPIs = retrofit.create(UploadAPIs.class);
Call<ResponseBody> call = uploadAPIs.uploadFile(descriptionPart, fileMulti);
call.enqueue(new Callback() {
#Override
public void onResponse(Call call, Response response) {
if (!response.isSuccessful()) {
// Handle the error
String error = response.errorBody().toString();
return false;
}else{
String responseStr = response.body().toString();
return true;
}
}
#Override
public void onFailure(Call call, Throwable t) {
}
});
}
I got response internal server error (Android studio Debugging mode)
Android okhttp3(When click button call uploadFile method)
public Boolean uploadFile(File file) {
OkHttpClient client = new OkHttpClient();
RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("multipart/form-data"), file))
.build();
Request request = new Request.Builder()
.url("http://xxxxxxxxxx/api/FileExplorerApi/FileUpload")
.post(body)
.addHeader("content-type", "multipart/form-data")
.addHeader("Content-Type", "multipart/form-data,image/gif")
.addHeader("cache-control", "no-cache")
.addHeader("Postman-Token", "58564c84-b8a7-4455-b346-4606ce696675")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(final Call call, final IOException e) {
// Handle the error
}
#Override
public void onResponse(final Call call, final Response response) throws IOException {
if (!response.isSuccessful()) {
// Handle the error
return false;
}else{
String responseStr = response.body().string();
return true;
}
// Upload successful
}
});
}
.net Web Api(Web api controller)
[HttpPost]
[Route("~/FileExplorerApi/FileUpload")]
public HttpResponseMessage Upload()
{
var request = HttpContext.Current.Request;
var file = request.Files["file"];
string filename = DateTime.Now.ToString("yyyyMMddHHmmssFFF_") + file.FileName;
file.SaveAs(#"c:\Projects\FileExplorer\Gif\" + filename);
return new HttpResponseMessage(HttpStatusCode.OK);
}
Postman Result
Finally, I got an answer. My .net project is MVC existing web project. I added API controller to that project that why I got that problem. I thought postman result is fine android also should be fine. My solution is to create the new .net web API only project.
The following answer is just sharing for my improvement.
I changed the new HTTP client loopj library that one is easier than previous both of the libraries.
RequestParams params = new RequestParams();
try {
params.put("file", file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
AsyncHttpClient client = new AsyncHttpClient();
client.post("http://xxxxxxxxxx/api/FileExplorerApi/Upload?sub=Boomerang", params, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
System.out.println("statusCode "+statusCode);//statusCode 200
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
}
});
I will upload a big size file that why I need to make configuration at .net MVC web API project. Following configuration need to do for upload upsize file.(Web.config)
<system.web>
<httpRuntime maxRequestLength="2097152"/>
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="1073741824"/>
</requestFiltering>
</security>
<system.webServer>

Android JSON POST with OKHTTP

I´m looking for a solution to implement a JSON-POST request with OKHTTP. I´ve got an HTTP-Client.java file which handles all the methods (POST, GET, PUT, DELETE) and in the RegisterActivity I´d like to POST the user-data (from the input fields) JSON-formatted to the server.
This is my HTTP-Client.java
public class HttpClient{
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
public static OkHttpClient client = new OkHttpClient.Builder()
.cookieJar(new CookieJar() {
private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
#Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
cookieStore.put(url.host(), cookies);
}
#Override
public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookies = cookieStore.get(url.host());
return cookies != null ? cookies : new ArrayList<Cookie>();
}
})
.build();
public static Call post(String url, String json, Callback callback) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body.create(JSON, json))
.build();
Call call = client.newCall(request);
call.enqueue(callback);
return call;
}
}
... and this is the onClick-Part from the RegisterActivity
btnRegRegister.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//TODO
String registerData = "{\"email\":\"" + etRegisterEmail.getText().toString() + "\",\"password\":\"" + etRegisterPasswort.getText().toString() + "\"}";
try {
HttpClient.post(ABSOLUTE_URL, registerData, new Callback(){
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String resp = response.body().string();
if (resp != null) {
Log.d("Statuscode", String.valueOf(response.code()));
Log.d("Body", response.body().string());
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
});
Everytime I start the app it crashes when I click the Register-Button caused by a FATAL EXPECTION 'android.os.NetworkOnMainThreadException'
I´ve alread read something about the AsyncTask but I don´t know exactly how to do this.
Try my code below
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
Map<String, String> params = new HashMap<String, String>();
params.put("msisdn", "123123");
params.put("name", "your name");
JSONObject parameter = new JSONObject(param);
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(JSON, parameter.toString());
Request request = new Request.Builder()
.url(url)
.post(body)
.addHeader("content-type", "application/json; charset=utf-8")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.e("response", call.request().body().toString());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
Log.e("response", response.body().string());
}
});
It's because you are trying to execute the HTTP query on the main thread (or UI thread). You shouldn't do a long task on the main thread because your app will hang, because the drawing routines are executed in that thread (hence his another name "UI Thread"). You should use another thread to make your request. For example:
new Thread(){
//Call your post method here.
}.start();
The Android asynctask is a simple class to do asynchronous work. It executes first his "onPreExecute" method on the calling thread, then his "doInBackground" method on a background thread, then his "onPostExecute" method back in the calling thread.
Try using Retrofit library for making Post request to the server. This provides a fast and reliable connection to the server.
You can also use Volley library for the same.

OkHttp image type

When I upload an image okHttp only accepts PNG? When I try jpg it denies.
This is my code:
public static Boolean uploadFile(final File file, final Context context) {
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
#Override
public void run() {
try {
final MediaType MEDIA_TYPE_JPG = MediaType.parse("image");
//employee verkrijgen
DataLayer dataLayer = new DataLayer(context);
Employee employee = dataLayer.getEmployee();
dataLayer.close();
//request body aanmaken
RequestBody formBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("ProfilePicture", file.getName(),
RequestBody.create(MEDIA_TYPE_JPG,file))
.addFormDataPart("api_token", employee.getApiToken())
.build();
Request request = new Request.Builder().url(static_urls.Employee.uploadProfilePicture(employee.getEmployeeId())).post(formBody).build();
OkHttpClient client = new OkHttpClient();
Response response = client.newCall(request).execute();
String responseString = response.body().string().substring(5000, response.body().string().length());
}
catch(Exception ex)
{
Log.e("upload",Log.getStackTraceString(ex));
}
}
});
return false;
}
What am I doing wrong?
use
MediaType.parse("image/jpeg");
Here is full list of mime types

Retrofit 2 file down/upload

I'm trying to down/upload a file with retrofit 2 but can't find any tutorials examples on how to do so.
My code for downloading is:
#GET("documents/checkout")
public Call<File> checkout(#Query(value = "documentUrl") String documentUrl, #Query(value = "accessToken") String accessToken, #Query(value = "readOnly") boolean readOnly);
and
Call<File> call = RetrofitSingleton.getInstance(serverAddress)
.checkout(document.getContentUrl(), apiToken, readOnly[i]);
call.enqueue(new Callback<File>() {
#Override
public void onResponse(Response<File> response,
Retrofit retrofit) {
String fileName = document.getFileName();
try {
System.out.println(response.body());
long fileLength = response.body().length();
InputStream input = new FileInputStream(response.body());
File path = Environment.getExternalStorageDirectory();
File file = new File(path, fileName);
BufferedOutputStream output = new BufferedOutputStream(
new FileOutputStream(file));
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
output.write(data, 0, count);
}
output.flush();
output.close();
} catch (IOException e) {
String logTag = "TEMPTAG";
Log.e(logTag, "Error while writing file!");
Log.e(logTag, e.toString());
}
}
#Override
public void onFailure(Throwable t) {
// TODO: Error handling
System.out.println(t.toString());
}
});
I've tried with Call and Call but nothing seems to work.
The server-side code writes the file's bytes into HttpServletResponse's output stream after setting the headers and mime type correctly.
What am I doing wrong?
Finally, the upload code:
#Multipart
#POST("documents/checkin")
public Call<String> checkin(#Query(value = "documentId") String documentId, #Query(value = "name") String fileName, #Query(value = "accessToken") String accessToken, #Part("file") RequestBody file);
and
RequestBody requestBody = RequestBody.create(MediaType.parse(document.getMimeType()), file);
Call<String> call = RetrofitSingleton.getInstance(serverAddress).checkin(documentId, document.getFileName(), apiToken, requestBody);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Response<String> response, Retrofit retrofit) {
System.out.println(response.body());
}
#Override
public void onFailure(Throwable t) {
System.out.println(t.toString());
}
});
Thanks!
Edit:
After the answer, downloading only yields a corrupted file (without the #Streaming), uploading doesn't as well. When I use the above code, the server returns a 400 error. After changing it to
RequestBody requestBody = RequestBody.create(MediaType.parse(document.getMimeType()), file);
MultipartBuilder multipartBuilder = new MultipartBuilder();
multipartBuilder.addFormDataPart("file", document.getFileName(), requestBody);
Call<String> call = RetrofitSingleton.getInstance(serverAddress).checkin(documentId, document.getFileName(), apiToken, multipartBuilder.build());
, the request executes but the backend doesn't seem to receive a file.
Backend code:
#RequestMapping(value = "/documents/checkin", method = RequestMethod.POST)
public void checkInDocument(#RequestParam String documentId,
#RequestParam String name, #RequestParam MultipartFile file,
#RequestParam String accessToken, HttpServletResponse response)
What am I doing wrong? I was able to use the backend from plain Java with the Apache HttpClient:
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.addBinaryBody("file", new File("E:\\temp\\test.jpg"));
HttpEntity httpEntity = builder.build();
System.out.println("HttpEntity " + EntityUtils.toString(httpEntity.));
HttpPost httpPost = new HttpPost(uri);
httpPost.setEntity(httpEntity);
Edit v2
For anyone interested, both up- and downloading work now: These are the solutions:
Service:
#GET("documents/checkout")
public Call<ResponseBody> checkout(#Query(value = "documentUrl") String documentUrl, #Query(value = "accessToken") String accessToken, #Query(value = "readOnly") boolean readOnly);
#Multipart
#POST("documents/checkin")
public Call<String> checkin(#Query(value = "documentId") String documentId, #Query(value = "name") String fileName, #Query(value = "accessToken") String accessToken, #Part("file") RequestBody file);
Download Code:
Call<ResponseBody> call = RetrofitSingleton.getInstance(serverAddress)
.checkout(document.getContentUrl(), apiToken, readOnly[i]);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Response<ResponseBody> response,
Retrofit retrofit) {
String fileName = document.getFileName();
try {
File path = Environment.getExternalStorageDirectory();
File file = new File(path, fileName);
FileOutputStream fileOutputStream = new FileOutputStream(file);
IOUtils.write(response.body().bytes(), fileOutputStream);
} catch (IOException e) {
Log.e(logTag, "Error while writing file!");
Log.e(logTag, e.toString());
}
}
#Override
public void onFailure(Throwable t) {
// TODO: Error handling
System.out.println(t.toString());
}
});
Upload Code:
Call<String> call = RetrofitSingleton
.getInstance(serverAddress).checkin(documentId,
document.getFileName(), apiToken,
multipartBuilder.build());
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Response<String> response,
Retrofit retrofit) {
// Handle response here
}
#Override
public void onFailure(Throwable t) {
// TODO: Error handling
System.out.println("Error");
System.out.println(t.toString());
}
});
For downloading, you can use ResponseBody as your return type --
#GET("documents/checkout")
#Streaming
public Call<ResponseBody> checkout(#Query("documentUrl") String documentUrl, #Query("accessToken") String accessToken, #Query("readOnly") boolean readOnly);
and you can get the ResponseBody input stream in your call back --
Call<ResponseBody> call = RetrofitSingleton.getInstance(serverAddress)
.checkout(document.getContentUrl(), apiToken, readOnly[i]);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Response<ResponseBody> response,
Retrofit retrofit) {
String fileName = document.getFileName();
try {
InputStream input = response.body().byteStream();
// rest of your code
Your upload looks okay at first glance if you server handles multipart messages correctly. Is it working? If not, can you explain the failure mode? You also might be able to simplify by not making it multipart. Remove the #Multipart annotation and convert #Path to #Body --
#POST("documents/checkin")
public Call<String> checkin(#Query("documentId") String documentId, #Query("name") String fileName, #Query("accessToken") String accessToken, #Body RequestBody file);
I am using retrofit 2.0.0-beta2 and I had an issue uploading image by using multipart request. I solved it by using this answer: https://stackoverflow.com/a/32796626/2915075
The key for me was to use standard POST with MultipartRequestBody instead of #Multipart annotated request.
Here is my code:
Retrofit service class
#POST("photo")
Call<JsonElement> uploadPhoto(#Body RequestBody imageFile, #Query("sessionId"));
Usage from activity, fragment:
RequestBody fileBody = RequestBody.create(MediaType.parse("image/jpeg"), imageFile);
MultipartBuilder multipartBuilder = new MultipartBuilder();
multipartBuilder.addFormDataPart("photo", imageFile.getName(), fileBody);
RequestBody fileRequestBody = multipartBuilder.build();
//call
mRestClient.getRetrofitService().uploadProfilePhoto(fileRequestBody, sessionId);
i have the same problems, and i found a solution to upload files, that described here
Is it possible to show progress bar when upload image via Retrofit 2
Also I had this problem, This is how i try to solve my problem (RETROFIT 2 )
//1. What We Need From Server ( upload.php Script )
public class FromServer {
String result;
}
//2. Which Interface To Communicate Our upload.php Script?
public interface ServerAPI {
#Multipart
#POST("upload.php")//Our Destination PHP Script
Call<List<FromServer>> upload(
#Part("file_name") String file_name,
#Part("file") RequestBody description);
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl("http://192.168.43.135/retro/") // REMEMBER TO END with /
.addConverterFactory(GsonConverterFactory.create())
.build();
}
//3. How To Upload
private void upload(){
ServerAPI api = ServerAPI.retrofit.create(ServerAPI.class);
File from_phone = FileUtils.getFile(Environment.getExternalStorageDirectory()+"/aa.jpg"); //org.apache.commons.io.FileUtils
RequestBody to_server = RequestBody.create(MediaType.parse("multipart/form-data"), from_phone);
api.upload(from_phone.getName(),to_server).enqueue(new Callback<List<FromServer>>() {
#Override
public void onResponse(Call<List<FromServer>> call, Response<List<FromServer>> response) {
Toast.makeText(MainActivity.this, response.body().get(0).result, Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<List<FromServer>> call, Throwable t) { }
});
}
//4. upload.php
<?php
$pic = $_POST['file_name'];
$pic = str_replace("\"", "", $pic); //REMOVE " from file name
if(file_exists($pic)){unlink($pic);}
$f = fopen($pic, "w");
fwrite($f,$_POST['file']);
fclose($f);
$arr[] = array("result"=>"Done");
print(json_encode($arr));
?>
You can refer tutorial for Image Download using Retrofit 2.0
For the time being you can refer following functions for image download:
void getRetrofitImage() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
RetrofitImageAPI service = retrofit.create(RetrofitImageAPI.class);
Call<ResponseBody> call = service.getImageDetails();
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Response<ResponseBody> response, Retrofit retrofit) {
try {
Log.d("onResponse", "Response came from server");
boolean FileDownloaded = DownloadImage(response.body());
Log.d("onResponse", "Image is downloaded and saved ? " + FileDownloaded);
} catch (Exception e) {
Log.d("onResponse", "There is an error");
e.printStackTrace();
}
}
#Override
public void onFailure(Throwable t) {
Log.d("onFailure", t.toString());
}
});
}
Following is the file handling part image download using Retrofit 2.0
private boolean DownloadImage(ResponseBody body) {
try {
Log.d("DownloadImage", "Reading and writing file");
InputStream in = null;
FileOutputStream out = null;
try {
in = body.byteStream();
out = new FileOutputStream(getExternalFilesDir(null) + File.separator + "AndroidTutorialPoint.jpg");
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
}
catch (IOException e) {
Log.d("DownloadImage",e.toString());
return false;
}
finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
int width, height;
ImageView image = (ImageView) findViewById(R.id.imageViewId);
Bitmap bMap = BitmapFactory.decodeFile(getExternalFilesDir(null) + File.separator + "AndroidTutorialPoint.jpg");
width = 2*bMap.getWidth();
height = 6*bMap.getHeight();
Bitmap bMap2 = Bitmap.createScaledBitmap(bMap, width, height, false);
image.setImageBitmap(bMap2);
return true;
} catch (IOException e) {
Log.d("DownloadImage",e.toString());
return false;
}
}
I hope it will help. All the best. Happy Coding :)

Uploading a large file in multipart using OkHttp

What are my options for uploading a single large file (more specifically, to s3) in multipart in Android using OKhttp?
Get OkHttp 2.1, and use MultipartBuilder.addFormDataPart() which takes the filename as a parameter.
/**
* Upload Image
*
* #param memberId
* #param sourceImageFile
* #return
*/
public static JSONObject uploadImage(String memberId, String sourceImageFile) {
try {
File sourceFile = new File(sourceImageFile);
Log.d(TAG, "File...::::" + sourceFile + " : " + sourceFile.exists());
//Determining the media type
final MediaType MEDIA_TYPE = sourceImageFile.endsWith("png") ?
MediaType.parse("image/png") : MediaType.parse("image/jpeg");
RequestBody requestBody = new MultipartBuilder()
.type(MultipartBuilder.FORM)
.addFormDataPart("member_id", memberId)
.addFormDataPart("file", "profile.png", RequestBody.create(MEDIA_TYPE, sourceFile))
.build();
Request request = new Request.Builder()
.url(URL_UPLOAD_IMAGE)
.post(requestBody)
.build();
OkHttpClient client = new OkHttpClient();
Response response = client.newCall(request).execute();
return new JSONObject(response.body().string());
} catch (UnknownHostException | UnsupportedEncodingException e) {
Log.e(TAG, "Error: " + e.getLocalizedMessage());
} catch (Exception e) {
Log.e(TAG, "Other Error: " + e.getLocalizedMessage());
}
return null;
}
#Edited for okhttp3:
compile 'com.squareup.okhttp3:okhttp:3.4.1'
RequestBody replaced by:
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("uploaded_file", filename, RequestBody.create(MEDIA_TYPE_PNG, sourceFile))
.addFormDataPart("result", "my_image")
.build();
#Uploaded Demo on GITHUB:
##I have added my answer for Multiple Image Upload :)
From the OkHttp Recipes page, this code uploads an image to Imgur:
private static final String IMGUR_CLIENT_ID = "...";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
RequestBody requestBody = new MultipartBuilder()
.type(MultipartBuilder.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"title\""),
RequestBody.create(null, "Square Logo"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"image\""),
RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
.url("https://api.imgur.com/3/image")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
You'll need to adapt this to S3, but the classes you need should be the same.
For okhttp 4.* use the MultipartBody.Builder:
fun postMultipart(url: String, text: String, imagePath: String, imageFileName: String): okhttp3.Response? {
val file = File(imagePath)
val fileRequestBody = file.asRequestBody("image/jpeg".toMediaType())
val requestBody = MultipartBody.Builder()
.addFormDataPart("text", text)
.addFormDataPart("image", imageFileName, fileRequestBody)
.build()
val request = getRequestBuilder(url)
.post(requestBody)
.build()
val client = OkHttpClient()
client.newCall(request).execute().use { response ->
return response
}
}
for okhttp 2.6.0 {
try {
File file = new File(Environment.getExternalStorageDirectory().getPath()+"/xxx/share/" + "ic_launcher.png");
String contentType = file.toURL().openConnection().getContentType();
RequestBody fileBody = RequestBody.create(MediaType.parse(contentType), file);
RequestBody requestBody = new MultipartBuilder()
.type(MultipartBuilder.FORM)
.addFormDataPart("fileUploadType","1")
.addFormDataPart("miniType",contentType)
.addFormDataPart("ext",file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf(".")))
.addFormDataPart("fileTypeName","img")
.addFormDataPart("Filedata","ss.png",fileBody)
.build();
Request request = new Request.Builder()
.url(Contains.MULTIPARTY_POST)
.post(requestBody)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
runOnUiThread(new Runnable() {
#Override
public void run() {
tvGetNews.setText("upload fail");
}
});
}
#Override
public void onResponse(Response response) throws IOException {
runOnUiThread(new Runnable() {
#Override
public void run() {
tvGetNews.setText("upload success");
}
});
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
In Android you usally part from an Uri. The problem when using large files is that you easily run into OutOfMemoryError if you try to read the full stream to a byte array (everything in memory) or you end up creating useless files with Uri stream. This is because RequestBody doesn't support creation from Stream (because sometimes OkHttp needs to read it many times, if you get a 30X redirect for instance) or Uri (because OkHttp is not an Android library).
But OkHttp provides the library Okio, with convenient classes emulating Streams (Source and Sink) and more convenient internal usage.
So, to create a BodyRequest form an Uri avoiding any OutOfMemoryError due to large files create it this way:
private static final MediaType MULTIPART_FOR_DATA = MediaType.parse("multipart/form-data");
private #NotNull RequestBody getFilePart(Uri largeFileUri) {
return new RequestBody() {
#Override
public MediaType contentType() {
return MULTIPART_FOR_DATA;
}
#Override
public void writeTo(#NotNull BufferedSink sink) throws IOException {
try (Source source = Okio.source(context.getContentResolver().openInputStream(mediaUri))) {
sink.writeAll(source);
}
}
};
}
Thank you to everyone posting and commenting in the folowing GitHub thread https://github.com/square/okhttp/issues/3585

Categories

Resources