Я загружаю фотографию на сервер с помощью HttpClient по умолчанию в Android SDK. Я хочу показать прогресс в пользовательском интерфейсе, есть ли способ узнать, сколько было загружено? Возможно ли это с помощью HttpUrlConnection?
Android - я хочу показать пользователю процесс загрузки файлов
Ответ 1
Для меня HTTPClient не работает. Байты, которые буферизируются в частях и отправляются как итоговые данные после флеш-вызова. То, что работало, это отправить его на уровень сокета.
Вы можете использовать HttpMultipartClient для этого (обновленная ссылка 30-10-2011): http://code.google.com/p/rainbowlibs/source/browse/android/trunk/rainbowlibs/src/it/rainbowbreeze/libs/data/HttpMultipartClient.java?spec=svn94&r=94
Укажите количество байтов для каждой части и обновите индикатор прогресса в цикле while:
while ((line = reader.readLine())!= null & &! headersEnd)
Вызовите HttpMultipartClient следующим образом:
HttpMultipartClient httpMultipartClient = new HttpMultipartClient("bluppr.com", "/api/order/create", 80);
FileInputStream fis = new FileInputStream(path + fileName);
httpMultipartClient.addFile(fileName, fis, fis.available());
httpMultipartClient.setRequestMethod("POST");
httpMultipartClient.send();
На стороне сервера используйте:
<?php
$target_path = "uploads/";
$target_path = $target_path . basename( $_FILES['uploadedfile']['name']);
if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
echo "The file ". basename( $_FILES['uploadedfile']['name'])." has been uploaded " .$_POST["order"]. " post";
} else{
echo "There was an error uploading the file, please try again!";
}
?>
Я использовал это для открыток Bluppr, работал как шарм. Если вам нужна дополнительная информация, дайте мне знать.
Ответ 2
1) Обязательно выполните загрузку в Службе с помощью собственного потока.
2) Чтобы получить прогресс: оберните свой InputStream в этом классе и используйте библиотеку httpmime.jar, которая поддерживает MultiPart для HttpClient. Я использовал поток, который проверяет ход выполнения и обновляет индикатор прогресса в уведомлении.
package com.hyves.android.service.upload;
import java.io.IOException;
import java.io.InputStream;
/**
* This outputstream wraps an existing outputstream and provides
* callbacks after certain amount of bytes to a HttpCallback
*
* @author tjerk
*/
public class ProgressNotifyingInputStream extends InputStream {
private InputStream wrappedStream;
private int count = 0;
private int totalSize;
/**
* Creates a new notifying outputstream which wraps an existing one.
* When you write to this stream the callback will be notified each time when
* updateAfterNumberBytes is written.
*
* @param stream the outputstream to be wrapped
* @param totalSize the totalsize that will get written to the stream
*/
public ProgressNotifyingInputStream(InputStream stream, int totalSize) {
if(stream==null) {
throw new NullPointerException();
}
if(totalSize == 0) {
throw new IllegalArgumentException("totalSize argument cannot be zero");
}
this.wrappedStream = stream;
this.totalSize = totalSize;
}
@Override
public int read() throws IOException {
count++;
return wrappedStream.read();
}
/**
* Get progress from 0 to 100
* @return
*/
public int getProgress() {
return count * 100 / totalSize;
}
}
Ответ 3
Мне понадобился процесс загрузки для изображения и не использовал HttpMultipartClient из-за проблем с реализацией (проблема с получением пакета через gradle и ошибки зависимостей). Другая проблема, с которой я столкнулся, - получить фактический размер файла, который я хотел загрузить.
Мои требования также включали загрузку в область уведомлений. Вот мое решение:
Получение размера изображения
protected int sizeOf(Bitmap data) {
/*
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return data.getAllocationByteCount();
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1) {
return data.getRowBytes() * data.getHeight();
} else {
return data.getByteCount();
}
// NONE OF THE ABOVE RETURN ACCURATE RESULTS!
// A Bitmap, when stored as a file takes up more room because it represents
// full pixel data and is not compressed on disk.
*/
byte[] bitmapdata = getBmpAsByteArray(data);
return (bitmapdata == null) ? 0 : bitmapdata.length;
}
AsyncHttpPostTask extends AsyncTask<UploadableImage, Integer, String>
AsyncHttpPostTask#onProgressUpdate
Эта функция вызывается из AsyncHttpPostTask#doInBackground
, которая вызывает обратный вызов для предупреждения активности изменения состояния.
@Override
protected void onProgressUpdate(Integer... progress) {
((ImageUploadActivity) activity).updateProgress(progress[0]);
}
AsyncHttpPostTask#doInBackground
Как я упоминал ранее, я не использовал HttpMultipartClient
, поэтому мне пришлось сортировать реализацию самостоятельно. Большая часть этого происходит от http://www.androidsnippets.com/multipart-http-requests
@Override
protected String doInBackground(InputStream... inStream) {
if (MainActivity.isDebugMode) {
Log.d(TAG, "doInBackground");
}
HttpURLConnection connection;
DataOutputStream outputStream;
InputStream inputStream;
String twoHyphens = "--";
String boundary = "----------MobileFormData";
String lineEnd = "\r\n";
String result;
int bytesRead, bytesAvailable, bufferSize;
byte[] buffer;
int maxBufferSize = 32768; // 2^15 = 32k -- http://stackoverflow.com/a/11221907/940217
try {
InputStream is = inStream[0];
totalSize = curUpImage.getFileSize();
Log.e(TAG, "Determined the file size to be " + totalSize + " bytes");
URL url = new URL(this.server);
connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setChunkedStreamingMode(maxBufferSize);
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("User-Agent", "Android Multipart HTTP Client 1.0");
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
outputStream = new DataOutputStream(connection.getOutputStream());
// Upload POST Data
Log.e(TAG, "Args: "+this.postArgs);
String[] posts = this.postArgs.split("&");
for (String post : posts) {
outputStream.writeBytes(twoHyphens + boundary + lineEnd);
String[] kv = post.split("=");
outputStream.writeBytes(String.format("Content-Disposition: form-data; name=\"%s\"", kv[0]));
outputStream.writeBytes(lineEnd);
outputStream.writeBytes(lineEnd);
outputStream.writeBytes(String.format("%s", kv[1]));
outputStream.writeBytes(lineEnd);
}
outputStream.writeBytes(twoHyphens + boundary + lineEnd);
outputStream.writeBytes("Content-Disposition: form-data; name=\"" + this.fileParamConst + "\"; filename=\"image.jpg\"" + lineEnd);
outputStream.writeBytes("Content-Type: image/jpeg" + lineEnd);
outputStream.writeBytes(lineEnd);
bytesAvailable = is.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
int totalByteRead = 0;
bytesRead = is.read(buffer, 0, bufferSize);
while (bytesRead > 0) {
totalByteRead += bytesRead;
Log.w(TAG, "totalByteRead: "+totalByteRead+", totalSize: "+totalSize);
publishProgress((int) ((totalByteRead / (float) totalSize) * 100));
outputStream.write(buffer, 0, bufferSize);
bytesAvailable = is.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = is.read(buffer, 0, bufferSize);
}
if (totalByteRead == 0){
Log.e(TAG, "Total bytes read from image file: "+totalByteRead);
} else {
Log.d(TAG, "Total bytes read from image file: "+totalByteRead);
}
outputStream.writeBytes(lineEnd);
outputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
inputStream = connection.getInputStream();
result = this.convertStreamToString(inputStream);
is.close();
inputStream.close();
outputStream.flush();
outputStream.close();
return result;
} catch (MalformedURLException e) {
result = "Error - Malformed URL";
e.printStackTrace();
} catch (FileNotFoundException e) {
result = "Error - Image file not found.";
e.printStackTrace();
} catch (IOException e) {
result = "Error - IO Exception.";
e.printStackTrace();
}
return result;
}
AsyncHttpPostTask#onPostExecute
Здесь я анализирую ответ JSON на сервере, чтобы проверить, удалось ли загрузить загрузку, а затем вернуть сообщение в Activity, которая контролирует уведомление.
@Override
protected void onPostExecute(String result) {
String resultString = null;
if (MainActivity.isDebugMode){
Log.d(TAG, "Async result: "+result);
}
boolean successful = false;
String[] errorMessages = null;
try {
JSONObject mainObject = new JSONObject(result);
String resultJsonString = mainObject.getString("result");
JSONArray messagesJsonArray = mainObject.getJSONArray("messages");
if (resultJsonString != null){
if (resultJsonString.equalsIgnoreCase("success")){
successful = true;
} else {
Log.e(TAG, "result was: "+resultJsonString);
}
}
errorMessages = new String[messagesJsonArray.length()];
for (int i = 0; i < messagesJsonArray.length(); i++){
errorMessages[i]= (String)messagesJsonArray.get(i);
}
} catch (JSONException e){
Log.e(TAG, "JSON Exception -- The string that I tried to parse was:\n"+result);
e.printStackTrace();
}
if (successful) {
Toast.makeText(this.activity, "Upload completed successfully!", Toast.LENGTH_SHORT).show();
resultString = "Upload complete.";
} else {
String eMessages;
if (errorMessages != null && errorMessages.length > 0){
eMessages = TextUtils.join(", ", errorMessages);
resultString = "Image upload failed:\n"+eMessages;
} else {
resultString = "Image upload failed!";
}
}
((ImageUploadActivity) activity).updateProgress(null);
((ImageUploadActivity) activity).setPostResult(resultString);
}
Отображение прогресса
В Activity, который отвечает за уведомление, у меня есть эта функция обратного вызова, которая вызывается из задачи async. Отображение прогресса здесь также может быть сделано с использованием одного из решений, обсуждаемых в блоге Джона Рассела. Эта операция запускается с режимом singleTop
, так что, когда она выводится на передний план с уведомлением, состояние сохраняется.
ImageUploadActivity # buildNotify
private void buildNotify(){
Intent resultIntent = new Intent(this, ImageUploadActivity.class);
// Because clicking the notification opens a new ("special") activity, there's
// no need to create an artificial back stack.
PendingIntent resultPendingIntent =
PendingIntent.getActivity(
this,
0,
resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
mNotifyManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this);
mBuilder.setContentIntent(resultPendingIntent);
mBuilder.setContentTitle("Image Upload")
.setContentText("Image upload in progress")
.setSmallIcon(android.R.drawable.ic_menu_upload);
}
ImageUploadActivity # UpdateProgress
Этот метод очищает прогресс до уведомления, а также от пользовательского интерфейса, содержащегося в пределах действия.
public void updateProgress(Integer progress){
this.currentProgress = progress;
if (uploadStatusTV != null && this.currentProgress != null){
currentStatus = "uploading image: "+this.currentProgress+"%";
uploadStatusTV.setText("uploading image: "+this.currentProgress+"%");
if (mBuilder == null){
buildNotify();
}
// Sets the progress indicator to a max value, the
// current completion percentage, and "determinate" state
mBuilder.setProgress(100, currentProgress, false);
// Displays the progress bar for the first time.
mNotifyManager.notify(notify_id, mBuilder.build());
} else if (uploadStatusTV != null){
return;
} else {
Log.e(TAG, "You should never see this message.");
finish();
}
}
Ответ 4
Или вы должны использовать AsyncTask для фактического процесса загрузки файла и использовать ProcessDialog для запуска и остановки процесса.
Вы можете увидеть этот код, http://github.com/narup/mymobile/blob/master/pbdroid/src/com/example/android/skeletonapp/StoreListActivity.java, который я написал для загрузки данных JSON по HTTP, и я использую диалог процесса.
Основная часть кода:
private class LoadStoresTask extends AsyncTask<String, Void, List<Store>> {
@Override
protected List<Store> doInBackground(String... params) {
return WsiStoresClient.connect(params[0]);
}
@Override
protected void onPostExecute(List<Store> result) {
dismissDialog(BUSY_DIALOG_KEY);
}
}
Ответ 5
Я написал пример того, как это сделать → http://toolongdidntread.com/android/android-multipart-post-with-progress-bar/
Ответ 6
Я не работал с этим API, но заметьте, что HttpClient не является специфичным для Android:
org.apache.http.client.HttpClient
Итак, если вы используете Google для "HttpClient progress", существует несколько сообщений, которые могут быть полезны.
Также учтите, что post Android Download Progress
Ответ 7
Я не использовал httpclient, но я сделал что-то вроде того, что вы хотите использовать AsyncTask
.
private class DownloadImageTask extends AsyncTask<String, Void,Bitmap>{
protected Bitmap doInBackground(String... urls) {
while (myProgress<length){
myProgress=myProgress+1;
myProgressBar.setProgress(myProgress);
}
return decodeImage(urls[0]);
}
protected void onPostExecute(Bitmap result) {
//dialog.dismiss();
imView.setImageBitmap(result);
}
protected void onPreExecute() {
/* Things to be done while execution of long running operation is
in progress. For example updating ProgressDialog */
dialog = ProgressDialog.show(BusinessCardActivity.this,
"Loading.........","Wait For Few Second", true);
}
}
См. в фоновом процессе. Я увеличиваю прогрессную панель и декодирую изображение, а в пост-исполнении я устанавливаю изображение.