Upload large file to server android - 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')
}
},

Related

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

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.

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.

downloaded images appears in recent files android

I am downloading some pics using OkHttp lib, everything is ok but all of them appears in recent files. Is there a way to disable my pics to be there.
My code is:
public static void downloadSimpleFile(String urlLink, String myPath, String fileName) {
OutputStream outputStream = new FileOutputStream(new File(myPath + fileName));
byte[] bytes = new byte[1024];
int read;
final OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(5, TimeUnit.SECONDS);
client.setReadTimeout(5, TimeUnit.SECONDS);
client.setWriteTimeout(5, TimeUnit.SECONDS);
Request request = new Request.Builder()
.url(urlLink)
.build();
try {
Response response = client.newCall(request).execute();
if (response != null) {
InputStream inputStream = response.body().byteStream();
if (inputStream != null) {
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
}
}
} catch (IOException e) {
//exception code treatement
}
}
EDIT:
like #Knossos mentioned in comments, I create this .nomedia file, but my pics still appearing, used code:
public static void createNoMediaFile() {
File file = new File(mypath + ".nomedia");
if (!file.exists()) {
try {
if (file.createNewFile()) {
//is created
}
} catch (IOException e) {
//exception
}
}
}

okhttp multipart image upload with file name

i am trying to upload a image to server from an android phone. this is what i have done so far
OkHttpClient client = new OkHttpClient();
MultipartBuilder builder = new MultipartBuilder();
builder.type(MultipartBuilder.FORM).addPart(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), requestPackage.getJsonParam().toString()));
for (int i = 0; i < requestPackage.getPics().size(); i++) {
builder.addPart(RequestBody.create(MediaType.parse("image/png"/* + i*/), new File(URI.create(requestPackage.getPics().get(i)))));
Log.i("image to upload",URI.create(requestPackage.getPics().get(i)).toString());
}
requestBody = builder.build();
Request request = new Request.Builder().url(requestPackage.getUri()).post(requestBody).build();
try {
response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
// System.out.println(response.body().string());
return response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
how do i add names to the different parts.because if there is no name(key) to them then how will server side guy store it?
Get OkHttp 2.1, and use MultipartBuilder.addFormDataPart() which takes the filename as a parameter.
The syntax seems to have changed a bit since the previous answers. I'm using OkHttp 3.2.0.
public void upload(String url, File file) throws IOException {
RequestBody formBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.getName(),
RequestBody.create(MediaType.parse("image/png"), file))
.addFormDataPart("other_field", "other_field_value")
.build();
Request request = new Request.Builder().url(url).post(formBody).build();
Response response = this.client.newCall(request).execute();
}
You can find all in the official document: https://github.com/square/okhttp/wiki/Recipes
Especially you will be interested in folowing piece from Posting a multipart request:
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();
Here is a complete solution, of how to upload a file using okhttp3.
Firstly, add a file picker with a button on click listener to your code like this:
A button to pick file:
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_choose_file:
showFileChooser();
break;
}
}
private String filePath = null;
private File sourceFile;
private static final int FILE_SELECT_CODE = 0;
private void showFileChooser() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(
Intent.createChooser(intent, "Select a File to Upload"),
FILE_SELECT_CODE);
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(this, "Please install a File Manager.",
Toast.LENGTH_SHORT).show();
}
}
Then handle onActivityResult like this:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case FILE_SELECT_CODE:
if (resultCode == RESULT_OK) {
// Get the Uri of the selected file
Uri uri = data.getData();
File file = new File(getCacheDir(), getFileName(uri));
int maxBufferSize = 1 * 1024 * 1024;
try {
InputStream inputStream = getContentResolver().openInputStream(uri);
Log.e("InputStream Size","Size " + inputStream);
int bytesAvailable = inputStream.available();
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
Log.e("File Size","Size " + file.length());
inputStream.close();
outputStream.close();
file.getPath();
Log.e("File Path","Path " + file.getPath());
file.length();
Log.e("File Size","Size " + file.length());
if(file.length() > 0){
sourceFile = file;
filePath = sourceFile.getPath();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
} else {
}
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
private String getMimeType(String path) {
FileNameMap fileNameMap = URLConnection.getFileNameMap();
String contentTypeFor = fileNameMap.getContentTypeFor(path);
if (contentTypeFor == null)
{
contentTypeFor = "application/octet-stream";
}
return contentTypeFor;
}
public String getFileName(Uri uri) {
String result = null;
if (uri.getScheme().equals("content")) {
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
} finally {
cursor.close();
}
}
if (result == null) {
result = uri.getPath();
int cut = result.lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
}
}
return result;
}
Finally, handle the file upload along with other needed information like this :
try {
UpdateInformation("yourEmailAddress", filePath, sourceFile);
} catch (IOException e) {
e.printStackTrace();
}
private void UploadInformation(String email, final String _filePath, final File file) throws IOException {
runOnUiThread(new Runnable() {
#Override
public void run() {
//show progress bar here
}
});
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build();
String mime = getMimeType(_filePath);
RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart("file", file.getName(),
RequestBody.create(MediaType.parse(mime), file))
.addFormDataPart("email", email)
.build();
okhttp3.Request request = new okhttp3.Request.Builder()
.url("yourEndPointURL")
.post(body)
.addHeader("authorization", "yourEndPointToken")
.addHeader("content-type", "application/json")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
call.cancel();
runOnUiThread(new Runnable() {
#Override
public void run() {
//hide progress bar here
}
});
}
#Override
public void onResponse(Call call, okhttp3.Response response) throws IOException {
try {
final String myResponse = response.body().string();
runOnUiThread(new Runnable() {
#Override
public void run() {
//hide progress bar here
//Cont from here
//Handle yourEndPoint Response.
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
Note: Don't forget to add this permission to the manifest file.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
You can use multipart like below to send multiple values in single request
HttpPost httppost = new HttpPost(mPostURL);
MultipartEntity entity = new MultipartEntity();
entity.addPart("value", new StringBody("upload", Charset.forName("UTF-8")));
File myFile = new File(mFilePath);
FileBody fileBody = new FileBody(filePath);
entity.addPart("file", fileBody);
entity.addPart("filename", new StringBody("fileName", Charset.forName("UTF-8")));
httppost.setEntity(entity);
HttpClient httpClient = new DefaultHttpClient();

Categories

Resources