Getting java.net.SocketTimeoutException alwayse in downloading files with OkHttp - android

I am using OkHttp to download a file from the server.
And this is my code:
I set read and write timeout for it:
public static final String API_BASE_URL = "http://XXXX:8080/tablet/";
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder()
.connectTimeout(60,TimeUnit.SECONDS)
.writeTimeout(60,TimeUnit.SECONDS);
This is my createServiceFile:
public static <S> S createServiceFile(Class<S> serviceClass, final String token) {
if (token != null) {
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", token)
.header("Accept", "text/html,application/xhtml+xml,application/pdf,application/xml;q=0.9," +
"image/webp,audio/mpeg ,image/png,audio/mp4,image/jpeg,*/*;q=0.8")
.method(original.method(), original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
}
OkHttpClient client = httpClient.build();
Retrofit retrofit = builder.client(client).build();
return retrofit.create(serviceClass);
}
And this is my code that I write the response to SD Card (in response contentLength is -1 because the server doesn't send it to me):
public DownloadRequest DownloadFile() {
//Creating folder for Application in SD Card if it's not created yet in App Class!
new java.io.File(G.DIR_APP).mkdirs();
new java.io.File(G.DIR_MEDIA).mkdirs();
APIService downloadService = ServiceGenerator.createServiceFile(APIService.class, G.TOKEN);
downloadService.downloadFileWithDynamicUrlSync(url)//set URL of the file
.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.io())
.delay(30, TimeUnit.MILLISECONDS)
.subscribe(new Observer<ResponseBody>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(ResponseBody responseBody) {
Log.i(TAG, "On Next And Current App is: " + fileName);
if (writeResponseBodyToDisk(responseBody)) {
Log.i(TAG, fileName + "Downloaded successful");
//This Download was successful Then try to set downloadResult true
result = true;
} else {
result = false;
}
}
#Override
public void onError(Throwable e) {
Log.i(TAG, "!!!!!!!!!!!!!!!!\n\nOn Error And Current App is: " +
fileName + " And Error is" + e.toString() + "\n\n!!!!!!!!!!!!!!!!\n\n");
//Set downloadResult false
result = false;
if (listener != null) {
listener.onDownloadCompleteListener(false);
}
}
#Override
public void onComplete() {
Log.i(TAG, "On Complete And Current App is: " + fileName);
if (listener != null) {
if (result) {
listener.onDownloadCompleteListener(true);
} else {
listener.onDownloadCompleteListener(false);
}
}
}
});
return this;
}
And this code will write response to disck:
private boolean writeResponseBodyToDisk(ResponseBody body) {
// Location to save downloaded file and filename
java.io.File file = new java.io.File(destinationPath + fileName + "." + extension);
try {
InputStream inputStream = null;
OutputStream outputStream = null;
int bytesBuffered = 0;
try {
byte[] fileReader = new byte[bufferSize];
downloadSize = 0;
inputStream = body.byteStream();
/*
* If append requested we have to append new bytes to
* Exists bytes on the storage
*/
if (appendNeed) {
outputStream = new FileOutputStream(file, true);
} else {
outputStream = new FileOutputStream(file);
}
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
bytesBuffered += read;
if (bytesBuffered > 1024 * 1024) {
// outputStream.write(fileReader, 0, read);
outputStream.flush();
bytesBuffered = 0;
}
outputStream.write(fileReader, 0, read);
downloadSize += read;
percent = (int) (100.0f * (float) downloadSize / totalSize);
if (listener != null) {
listener.onDownloadPercentListener(percent);
}
}
outputStream.flush();
inputStream.close();
outputStream.close();
return true;
} catch (IOException e) {
Log.i(TAG, "Error in download" + e.toString());
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
Log.i(TAG, "Error in download" + e.toString());
return false;
}
}
But I get false from this method (writeResponseBodyToDisk)with error of
java.net.SocketTimeoutException while the file is downloaded correctly!
I think this problem it about contentLength that is -1 for me.
Thank you for your answers.

Related

Is it impossible to dump a database from a server?

when I upload db file to server using php and recover that file, the database date in android device explorer is updated. but the contents is not updated. I dont understand...
time is updated but not contents... what's going on? using retrofit2 for upload/download, the result log is success, and the file size is right "4kb". is there anyone? I'm so alone.... because when I ask a question, there is no one who answered to me....
ps : the file permissions are like this "-rw-rw----"
and here is my code to dump downloaded file to original file
private boolean writeResponseBodyToDisk(ResponseBody body) {
try {
// todo change the file location/name according to your needs
File futureStudioIconFile = new File(file.getPath());
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
inputStream = body.byteStream();
outputStream = new FileOutputStream(futureStudioIconFile);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
Log.d("TAG", "file download: " + fileSizeDownloaded + " of " + fileSize);
}
outputStream.flush();
return true;
} catch (IOException e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return false;
}
}
and here is response about download. please help me....
responseBodyCall = retrofitInterface.download("savelocation");
responseBodyCall.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d("Success", "success "+response.code());
Log.d("Success", "success "+response.message());
Log.d("Success", "downloaded");
boolean isSuccess = writeResponseBodyToDisk(response.body());
if(isSuccess) Toast.makeText(getApplicationContext(),"Success",Toast.LENGTH_LONG).show();
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.d("failure", "message = " + t.getMessage());
Log.d("failure", "cause = " + t.getCause());
}
});

Upload large file to server android

I tried to upload a large file to a server (the max file size supported is 128Mb). If I correctly understood, the server can receive large files (for example 500Mb) but only if these files are sent in chunks.
I wrote the method bellow and expected it to upload video files in chunks to the server. However, I am getting the following error from the server:
413 Request Entity Too Large
Bellow is my method:
public static void uploadFile(Context context, Uri videoUri, String fileName, File file) throws FileNotFoundException {
int CHUNK_SIZE = 2048;
ContentResolver contentResolver = context.getContentResolver();
final String contentType = contentResolver.getType(videoUri);
final AssetFileDescriptor fd = contentResolver.openAssetFileDescriptor(videoUri, "r");
if (fd == null) {
try {
throw new FileNotFoundException("could not open file descriptor");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
RequestBody videoFile = new RequestBody() {
#Override
public long contentLength() {
return fd.getDeclaredLength();
}
#Override
public MediaType contentType() {
return MediaType.parse(contentType);
}
#Override
public void writeTo(BufferedSink sink) throws IOException {
Source source = null;
try {
source = Okio.source(file);
long total = 0;
long read;
while ((read = source.read(sink.buffer(), CHUNK_SIZE)) != -1) {
total += read;
sink.flush();
Log.e("progress", String.valueOf(total/100/100/100));
//this.listener.transferred(total);
}
} finally {
Util.closeQuietly(source);
}
}
};
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("video/*", fileName, videoFile)
.build();
Request request = new Request.Builder()
.url("https://test.ua/api/upload")
.post(requestBody)
.header("Authorization", Utils.getHeaderToken())
.build();
OkHttpClient client = new OkHttpClient.Builder().build();
client.newCall(request).enqueue(new Callback() {
#Override public void onFailure(Call call, IOException e) {
try {
fd.close();
} catch (IOException ex) {
e.addSuppressed(ex);
}
Log.e("asdasd", "failed", e);
}
#Override public void onResponse(Call call, Response response) throws IOException {
Log.e("success", String.valueOf(response.code()));
InputStream in = response.body().byteStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String result, line = reader.readLine();
result = line;
while((line = reader.readLine()) != null) {
result += line;
}
Log.e("success", String.valueOf(result));
fd.close();
}
});
}
UPD:
server use laravel-chunk-upload and they have this settings in frontend:
dropzoneOptions: {
url: '/upload',
dictDefaultMessage: 'Click here',
chunksUploaded: function (file, done) {
done()
},
thumbnailWidth: 150,
maxFilesize: 250,
chunking: false,
chunkSize: 1000000, // Bytes
required: true,
maxFiles: 1,
acceptedFiles: 'video/*',
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
},

Download HTML file from URL using Retrofit

I am getting a URL in response. I want to download the html of that URL so that user can see it offline also. Its a recyclerView in which each items contain a URL. So when user clicks on the URL of one item it should save it in external disk.
Below is the code:
NewsAdapter:
case R.id.save:
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.nytimes.com/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
Log.i("Retrofit build", "initiated");
ApiInterface retrofitInterface = retrofit.create(ApiInterface.class);
final Call< ResponseBody > call = retrofitInterface.downloadFileWithDynamicUrlSync("2017/09/13/us/nursing-home-deaths-florida.html");
Log.i("Retrofit req execute", "initiated");
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
boolean writtenToDisk = false;
try {
writtenToDisk = writeResponseBodyToDisk(call.execute().body());
} catch (IOException e) {
e.printStackTrace();
}
;
Log.d("success", "file download was a success? " + writtenToDisk);
return null;
}
}.execute();
break;
private boolean writeResponseBodyToDisk(ResponseBody body) {
try {
// todo change the file location/name according to your needs
File futureStudioIconFile = new File(Environment.DIRECTORY_DOWNLOADS + File.separator + "Future Studio Icon.png");
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
inputStream = body.byteStream();
outputStream = new FileOutputStream(futureStudioIconFile);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
Log.d("filedownload", "file download: " + fileSizeDownloaded + " of " + fileSize);
}
outputStream.flush();
return true;
} catch (IOException e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return false;
}
}
ApiInterface:
// option 2: using a dynamic URL
#Streaming
#GET
Call<ResponseBody> downloadFileWithDynamicUrlSync(#Url String fileUrl);
I am also getting the error:
Failed to invoke public com.squareup.okhttp.ResponseBody() with no args
Can someone tell me how to implement it correctly.
Use URL with domain name to download file.Remove streaming annotation don't need that.
You are not receiving file body as you are not using complete URL.
Create an interface like this
#GET
Call<ResponseBody> downloadFile(#Url String url);
Then use this code :
public void onResponse(Call<ResponseBody> call, Response<ResponseBody>
response)
{
if (response.isSuccessful()) {
String filePath = Utils.downloadFile(response.body(),"filename");
}
}
public String downloadFile(ResponseBody body, String fileName) {
String filePath = null;
try {
int count;
byte data[] = new byte[1024 * 4];
InputStream bis = new BufferedInputStream(body.byteStream(), 1024 * 8);
File storageDir = new File(Environment.getExternalStorageDirectory(), "AppName");
if (!storageDir.exists()) {
storageDir.mkdirs();
}
File outputFile = new File(storageDir, fileName);
OutputStream output = new FileOutputStream(outputFile);
while ((count = bis.read(data)) != -1) {
output.write(data, 0, count);
}
output.flush();
output.close();
bis.close();
filePath = outputFile.getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
}
return filePath;
}

how to getting ContentLength in Retrofit

I am using a service in my app that I used to retrofit to download some "apk" files in the background.
I want to check downloaded file size with received file size to make sure I get "apk" completely.
but when i use response.body().contentLength() it is -1!
This is my code:
private void downloadAppFromServer(String url, final String fileId) {
APIService downloadService = ServiceGenerator.createServiceFile(APIService.class, "181412655", "181412655");
Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(url);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, final Response<ResponseBody> response) {
if (response.isSuccess()) {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
Log.d("LOGO", "server contacted and has file");
Log.d("LOGO", "FILE SIZE: " + response.body().contentLength());
boolean writtenToDisk = writeResponseBodyToDisk(response.body(), fileId);
Log.d("LOGO", "file download was a success? " + writtenToDisk);
return null;
}
}.execute();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
});
}
And this is my writeToDisk Method:
private boolean writeResponseBodyToDisk(ResponseBody body, String fileId) {
try {
// Location to save downloaded file and filename
File DownloadFile = new File(G.DIR_APK + "/" + fileId + ".apk");
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
inputStream = body.byteStream();
Log.d("LOGO", "file size is: " + fileSize ); //This is -1 !
outputStream = new FileOutputStream(DownloadFile);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
}
outputStream.flush();
if (fileSize == fileSizeDownloaded) {
return true;
} else {
return false;
}
} catch (IOException e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return false;
}
}
-1 means that web server doesnt give you any information about length of file.
Retrofit gets header content-length. If it doesnt exist response.body().contentLength() returns -1.
Try to make HEAD HTTP request to the same URL, it might return the Content-Length.

How to save a file as apk without increasing in size in Android (can not install it)

I try to download file from server and save it in internel storage and then install it.
I downloaded it using retrofit by this code
Call<ResponseBody> call = apiService.getNewVersion();
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call,
Response<ResponseBody> response) {
if (response.isSuccessful() ) {
Log.d("1", "server contacted and has file");
boolean writtenToDisk =
writeResponseBodyToDisk(response.body());
Log.d("2", "file download was a success? " + writtenToDisk);
} else {
Log.d("3", "server contact failed");
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e("4", "error");
}
});
and I saved it by this code
private boolean writeResponseBodyToDisk(ResponseBody body) {
try {
// todo change the file location/name according to your needs
File futureStudioIconFile = new
File(context.getExternalFilesDir(null) + File.separator + "app-debug.apk");
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
inputStream = body.byteStream();
outputStream = new FileOutputStream(futureStudioIconFile);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
Log.d("TAG", "file download: " + fileSizeDownloaded + " of "
+ fileSize);
}
outputStream.flush();
return true;
} catch (IOException e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return false;
}
}
its original 4.56MB but it shows 6.08MB in Android storage.
When I open it to install it shows
version N /A size N/ A
and when I try to install it, it shows this parse problem
Do you have any idea to solve this problem?
How to remove extra bytes?
You should make sure that you use HttpResponseMessage.content in your web service method because a normal return will not be a standard size for the file that you want:
[HttpGet]
[Route("api/Depository/DownloadFileFTP")] // /{pass}
public HttpResponseMessage DownloadFileFTP() //string pass
{
HttpResponseMessage result = null;
string ftphost = "*******";
string ftpfilepath = "/app-debug.apk";
byte[] fileData;
string ftpfullpath = "ftp://" + ftphost + ftpfilepath;
using (WebClient request = new WebClient())
{
request.Credentials = new NetworkCredential("**", "****");
fileData = request.DownloadData(ftpfullpath);
}
byte[] bytes = fileData;
result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new ByteArrayContent(bytes);
result.Content.Headers.ContentDisposition = new
System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "app.apk";
return result;
}

Categories

Resources