I'm trying to upload a file to the server using multipart form.
Since API 23 Android has deprecated the Apache HTTP library.
I switched to using OkHttp to do my file uploads like so:
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(name, fileName, requestBodyPart)
.build();
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
And the requestBodyPart is:
requestBodyPart = new RequestBody() {
#Override
public MediaType contentType() {
return MediaType.parse(contentType);
}
#Override
public void writeTo(BufferedSink sink) throws IOException {
try {
if (sink.writeAll(Okio.source(inputStream)) == 0) {
throw new IOException("Empty File!");
}
} finally {
MiscUtils.closeCloseable(inputStream);
}
}
};
However, it seems like OkHttp is not that great when it comes to file uploads. Lots of timeouts and seems to be creating several layers of abstractions (sources and sinks) and has this AsyncTimeout that fires while the file data is still being written over the socket.
Are there any recommendations for doing Multipart File Uploads from Android that work with API 23 onwards. I know I can include the HTTP legacy library but since it was removed I would prefer not to do that. Or is there a way I can improve the performance of OkHttp?
This should work
String name = ...
File file = ...
MediaType mediaType = MediaType.parse(...)
OkHttpClient httpClient = ...
Request request = new Request.Builder()
.url(url)
.post(new MultipartBuilder().type(MultipartBuilder.FORM)
.addFormDataPart(name,
file.getName(),
RequestBody.create(mediaType, file))
.build())
.build();
try {
Response response = httpClient.newCall(request).execute();
} catch (IOException e) {
// handle error
}
Related
I use OKHTTP3 library to upload files to my http file server.
I found this code to do this and it works fine.
But I also want to just create a new folder without file.
Does anybody know how to create the request?
OkHttpClient client = new OkHttpClient.Builder()
.authenticator(new Authenticator() {
#Override
public Request authenticate(Route route, Response response) throws IOException {
String credential = Credentials.basic(username,password);
return response.request().newBuilder()
.header("Authorization", credential)
.build();
}
})
.build();
RequestBody formBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.getName(),
RequestBody.create(MediaType.parse("text/plain"), file))
.addFormDataPart("other_field", "other_field_value")
.build();
Request request = new Request.Builder().url(url).post(formBody).build();
Response response = client.newCall(request).execute();
My Http File Server is in default configuration.
I didn't set up any script, because I don't understand the process(see on https://rejetto.com/wiki/index.php?title=HFS:_Event_scripts)
Thank you
I have a REDCap project complete setup on Redcap console .
API token generated .
Record saving working from REDCap .
Also from browser tool.
But when I call it from Android App it returns 403 forbidden .
is there anything like set permission for a user.
Also same is working perfectly from ios app .
HashMap<String, String> params = new HashMap<String, String>();
params.put("token","MY TOKEN");
params.put("content","record");
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(JSON, String.valueOf(params));
Request request = new Request.Builder()
.url("MY_URL")
.post(body)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(com.squareup.okhttp.Request request, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(com.squareup.okhttp.Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
} else {
// do something wih the result
Log.d("check ok http response ", response.toString());
}
}
});
From Browser tool if I put same URL with selecting POST and set only two params token and content , it return 200 OK .
But from Android it returns 403 . Please help , I have tried several methods in android code .
You're doing this:
RequestBody body = RequestBody.create(JSON, String.valueOf(params));
that's not a valid form body. Do this:
FormBody.Builder formBuilder = new FormBody.Builder()
.add("token","MY TOKEN").add("content","record");
and then
Request request = new Request.Builder()
.url("MY_URL")
.post(formBuilder.build())
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.build();
Generated the code below by postman:
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("multipart/form-data; boundary=---011000010111000001101001");
RequestBody body = RequestBody.create(mediaType, "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"[object Object]\"\r\nContent-Type: false\r\n\r\n\r\n-----011000010111000001101001--");
Request request = new Request.Builder()
.url("http://foobar.com/newsfeed/photo")
.post(body)
.addHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001")
.addHeader("x-access-token", "MczCvMEbllNhGaMwEDnGXuQjrwBAYuYleFlgsUZDWRYbVaohpEGgofonYcvHsgPaTnbzHxCvJWalYFTY")
.addHeader("accept-language", "ru")
.build();
Response response = client.newCall(request).execute();
This request make a file on Server with 0 Kb size. and I couldn't put a file. So, I have to put a file like this:
RequestBody body = RequestBody.create(mediaType, new File(filename));
But I got TimeOutExaption.
How to put a file by this kind of Rest API?
I found a way to multipart file upload by Ion.
Ion.with(context).load(url).setHeader("x-access-token", token)
.setMultipartParameter("x-access-token", token)
.setMultipartContentType("multipart/form-data")
.setMultipartFile("image", "image/jpeg", file)
.asString().withResponse().setCallback(new FutureCallback<Response<String>>() {
#Override
public void onCompleted(Exception e, Response<String> result) {
if (result != null)
onSuccess(result.getResult(), result.getHeaders().code());
}
});
Rest API:
I'm writing an android app that needs to communicate with a server. I'm struggling to make a MultipartBody for OkHttp3 to use. My code looks like this:
OkHttpClient client = new OkHttpClient();
try
{
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("someKey1", "someString1")
.addFormDataPart("someKey2", "someString2")
.build();
System.out.println(requestBody.toString());
Request request = new Request.Builder()
.url("server url")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
return response;
}
catch (IOException e)
{
e.printStackTrace();
return false;
}
This is pretty much the standard code everyone uses. The server receives the request, but I can't get the MultipartBody from the request. When the multipart body is printed, it looks like this:
I/System.out: okhttp3.MultipartBody#14040826
So my questions are: Why doesn't it contain my data and should it? Am I missing a library (I included compile 'com.squareup.okhttp3:okhttp:3.2.0' in the gradle and assume MultipartBody is part of this library)? What is a MultipartBody supposed to look like?
I tried sending a FormBody as well but it looks the same.
Remove okhttp3.Response response = client.newCall(request).execute();
okhttp3.Call call = client.newCall(request);
call.enqueue(new okhttp3.Callback() {
#Override
public void onFailure(okhttp3.Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
}
});
I'm trying to configure cache with Retrofit 1.9.0 and OkHtttp 2.5.0.
Here is how I provide OkHttpClient for my RestAdapter:
#Provides
#Singleton
public OkHttpClient provideOkHttpClient() {
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setConnectTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
okHttpClient.setReadTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
okHttpClient.setWriteTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
File cacheDir = new File(context.getCacheDir(), "http");
final Cache cache = new Cache(cacheDir, DISK_CACHE_SIZE_IN_BYTES);
okHttpClient.setCache(cache);
okHttpClient.interceptors().add(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(request);
Response finalResponse = response.newBuilder()
.header("Cache-Control", String.format("public, max-stale=%d", 604800))
.build();
Log.d("OkHttp", finalResponse.toString());
Log.d("OkHttp Headers", finalResponse.headers().toString());
return finalResponse;
}
});
return okHttpClient;
}
I did not forget to setClient on RestAdapter.Builder. Also made sure, that I'm actually using instance of RestAdapter with this client set.
Even checked if the files are created under "http" folder. They are.
However after I turn of WIFI and reload my screen I end up in OnError callback of Observable endpoint with this message:
retrofit.RetrofitError: failed to connect to /10.40.31.12 (port 8888) after 10000ms: connect failed: ENETUNREACH (Network is unreachable)
DISCLAIMER: I should probably mention that the final Observable is combined from 5 others, with flatMap and zip on the way.
I think I have an answer. Short one is: "Cannot be done if server sends no-cache header in response".
If you want the longer one, details are below.
I've made a sample app comparing 2 backends. Lets call them Backend A, and Backend B. A was giving me troubles so I've decided to check on B.
A returns CacheControl = "no-cache, no-transform, max-age=0"
B returns Cache-Control = „public" response header
I did the same setup for both backends, just different urls.
private void buildApi() {
Gson gson = new GsonBuilder().create();
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setConnectTimeout(10, TimeUnit.SECONDS);
File cacheDir = new File(getCacheDir(), "http");
final Cache cache = new Cache(cacheDir, 1000000 * 10);
okHttpClient.setCache(cache);
okHttpClient.interceptors().add(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Log.d("OkHttp REQUEST", request.toString());
Log.d("OkHttp REQUEST Headers", request.headers().toString());
Response response = chain.proceed(request);
response = response.newBuilder()
.header("Cache-Control", String.format("public, max-age=%d, max-stale=%d", 60, RESPONSE_CACHE_LIFESPAN_IN_SECONDS))
.build();
Log.d("OkHttp RESPONSE", response.toString());
Log.d("OkHttp RESPONSE Headers", response.headers().toString());
return response;
}
});
RestAdapter.Builder builder = new RestAdapter.Builder()
.setConverter(new StringGsonConverter(gson))
.setClient(new OkClient(okHttpClient))
.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
if (isNetworkAvailable()) {
request.addHeader("Cache-Control", "public, max-age=" + 60);
} else {
request.addHeader("Cache-Control", "public, only-if-cached, max-stale=" + RESPONSE_CACHE_LIFESPAN_IN_SECONDS);
}
}
});
builder.setEndpoint("http://this.is.under.vpn.so.wont.work.anyway/api");
A_API = builder.build().create(AApi.class);
builder.setEndpoint("http://collector-prod-server.elasticbeanstalk.com/api");
B_API = builder.build().create(BApi.class);
}
Did both calls, then disabled wifi.
Cache worked fine for B, but A thrown 504 Unsatisfiable Request (only-if-cached)
It seems that overwritting headers won't help in that case.
You should rewrite your Request instead of the Response. For reference, see the docs on rewriting requests. Note you, can also use the CacheControl class instead of building your own header if you want. Your interceptor should look something like --
okHttpClient.interceptors().add(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request cachedRequest = request.newBuilder()
.cacheControl(new CacheControl.Builder()
.maxStale(7, TimeUnit.DAYS)
.build())
.build();
return chain.proceed(cachedRequest);
}
});