Android - я хочу показать пользователю процесс загрузки файла - PullRequest
25 голосов
/ 21 января 2010

Я загружаю фото на сервер через HttpClient по умолчанию в Android SDK. Я хочу показать прогресс в пользовательском интерфейсе, есть ли способ узнать, сколько было загружено? Возможно ли это с помощью HttpUrlConnection?

Ответы [ 7 ]

14 голосов
/ 22 сентября 2010

Для меня 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, работал как шарм. Если вам нужна дополнительная информация, дайте мне знать.

3 голосов
/ 24 мая 2011

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;
    }

}
2 голосов
/ 03 июня 2014

Мне нужен был процесс загрузки изображения, и я не использовал 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, отвечающей за уведомление, у меня есть функция обратного вызова, которая вызывается из асинхронной задачи. Отображение прогресса здесь также можно сделать, используя одно из решений, обсуждавшихся в сообщении Джона Рассела в блоге . Это действие запускается в режиме 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();
    }
}
1 голос
/ 26 января 2010

Или вы должны использовать 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);
}

}
0 голосов
/ 26 июня 2011

Я написал пример, как именно это сделать -> http://toolongdidntread.com/android/android-multipart-post-with-progress-bar/

0 голосов
/ 25 августа 2010

Я не использовал 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);          
                }
             }

Смотрите в фоновом процессе, я увеличиваю индикатор выполнения и декодирую изображение, а в пост-выполнении я устанавливаю изображение.

0 голосов
/ 21 января 2010

Я не работал с этим API, но обратите внимание, что HttpClient не специфичен для Android:

org.apache.http.client.HttpClient

Так что, если вы гуглите "Прогресс HttpClient", есть ряд постов, которые могут быть полезны.

Также рассмотрим этот пост Прогресс загрузки Android

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...