Some time ago I got a task to create a file uploader for Android. Writing file uploader code it not difficult and I’ve done it quickly. To indicate the uploading progress I’ve shown standard rotating progress control while uploading.
But it’s not quite clear for users. Much better is to show the usual progress bar with bytes uploaded and total size. So I decided to implement this user friendliness.
Source code of BackgroundUploader class is here:
To execute file uploader the next code should be used:
I wrote the code based on the DNDApplet.java.
But it’s not quite clear for users. Much better is to show the usual progress bar with bytes uploaded and total size. So I decided to implement this user friendliness.
class BackgroundUploader extends AsyncTask<Void, Integer, Void> implements DialogInterface.OnCancelListener {
private ProgressDialog progressDialog;
private String url;
private File file;
public BackgroundUploader(String url, File file) {
this.url = url;
this.file = file;
}
@Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(context);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("Uploading...");
progressDialog.setCancelable(false);
progressDialog.setMax((int) file.length());
progressDialog.show();
}
@Override
protected Void doInBackground(Void... v) {
HttpURLConnection.setFollowRedirects(false);
HttpURLConnection connection = null;
String fileName = file.getName();
try {
connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("POST");
String boundary = "---------------------------boundary";
String tail = "\r\n--" + boundary + "--\r\n";
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
connection.setDoOutput(true);
String metadataPart = "--" + boundary + "\r\n"
+ "Content-Disposition: form-data; name=\"metadata\"\r\n\r\n"
+ "" + "\r\n";
String fileHeader1 = "--" + boundary + "\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile\"; filename=\""
+ fileName + "\"\r\n"
+ "Content-Type: application/octet-stream\r\n"
+ "Content-Transfer-Encoding: binary\r\n";
long fileLength = file.length() + tail.length();
String fileHeader2 = "Content-length: " + fileLength + "\r\n";
String fileHeader = fileHeader1 + fileHeader2 + "\r\n";
String stringData = metadataPart + fileHeader;
long requestLength = stringData.length() + fileLength;
connection.setRequestProperty("Content-length", "" + requestLength);
connection.setFixedLengthStreamingMode((int) requestLength);
connection.connect();
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes(stringData);
out.flush();
int progress = 0;
int bytesRead = 0;
byte buf[] = new byte[1024];
BufferedInputStream bufInput = new BufferedInputStream(new FileInputStream(file));
while ((bytesRead = bufInput.read(buf)) != -1) {
// write output
out.write(buf, 0, bytesRead);
out.flush();
progress += bytesRead;
// update progress bar
publishProgress(progress);
}
// Write closing boundary and close stream
out.writeBytes(tail);
out.flush();
out.close();
// Get server response
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = "";
StringBuilder builder = new StringBuilder();
while((line = reader.readLine()) != null) {
builder.append(line);
}
} catch (Exception e) {
// Exception
} finally {
if (connection != null) connection.disconnect();
}
return null;
}
@Override
protected void onProgressUpdate(Integer... progress) {
progressDialog.setProgress((int) (progress[0]));
}
@Override
protected void onPostExecute(Void v) {
progressDialog.dismiss();
}
@Override
public void onCancel(DialogInterface dialog) {
cancel(true);
dialog.dismiss();
}
}
* This source code was highlighted with Source Code Highlighter.
To execute file uploader the next code should be used:
new BackgroundUploader(url, file).execute();
I wrote the code based on the DNDApplet.java.
Man, thank you so much for posting this code! I have been playing with my own for a couple days and was about to pull my hair out. You have save me much more frustration by sharing your work, you rock!
ReplyDelete~Stephen
You're welcome.
ReplyDeletethank you, this is a very useful code
ReplyDeleteThanks DELIMITRY, Nice Share and great Code
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteYou are awesome ! BTW it throws "ECONNRESET (Connection reset by peer)" exception for large files.
ReplyDeleteChanging connection.setRequestMethod("POST"); to connection.setRequestMethod("PUT"); fixes "ECONNRESET " exception !
ReplyDeleteOh, thank you for info :)
Deletewhere can i download the source code
ReplyDeleteJust copy it from the blog post.
DeleteHey, thanks for the code, I'm having a fatal exception:
ReplyDeleteon progressDialog = new ProgressDialog(myContext);
"Can't create handler inside thread that has not called Looper.prepare()"
I'm sending the context via a parameter (added a Context parameter on the constructor). Here's the code, not sure how to solve this, I tried creating calling runOnUiThread but doesn't let me do that not sure why. My modified code:
private Context myContext;
public BackgroundUploader(String url, File file, Context context) {
this.url = url;
this.file = file;
myContext = context;
}
@Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(myContext); // I have the exception here.
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("Uploading...");
progressDialog.setCancelable(false);
progressDialog.setMax((int) file.length());
progressDialog.show();
}
How can I send a string, suppose str, alongwith the multipart data?
ReplyDeleteHow can I send a string, suppose str, alongwith the multipart data?
ReplyDeleteGreat article, even through posted from 2011, effectively solved my problem of httpurlconnection upload progress issue.
ReplyDeleteThank you :)
DeleteThank you for sharing your code - helped me a lot!
ReplyDeletethanks very much!
ReplyDeletethanks very much! I was thinking whether it's possible...
ReplyDeleteIt seems the key point is that we should connect at first and then open the outputstream and write data.
This comment has been removed by the author.
ReplyDeleteCan make a server side in PHP to receive these files?? if yes, how?
ReplyDeletePlease take a look at the server code in this answer: http://stackoverflow.com/a/5034974/821093
DeleteThank's very much!
Deleteit's not working.
DeleteI'm getting this error when send a file after other file sended with sucess
ReplyDeletejava.net.SocketException: sendto failed: EPIPE (Broken pipe)
what is it? how i solve this?? And other question, this PHP script http://stackoverflow.com/a/5034974/821093 support for unlimited users send files at the same time?? or only works with one image per time?
other bug I found is that when he sends a larger image than 2MB he says he sends everything right without error but the image does not appear on the server, and when you send images smaller than 2MB it appears normally
DeleteFor someone who have same problem in android, this is not a problem with the code, is only bad configuration of php config, just search to upload_max_filesize and set for the max size who want..
DeletePlease provide valid php script for uploading...
ReplyDeleteHow can we modify code to include FTP IP address, username and password? Does this code work only on a public access webserver only?
ReplyDelete