Android - 我想向用户显示文件上传进度

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2105364/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 04:33:43  来源:igfitidea点击:

Android - I want to show file upload progress to the user

android

提问by fhucho

I upload photo to the server via the default HttpClient in Android SDK. I want to show progress in the user interface, is there a way to find out how much has been uploaded? Is it possible with HttpUrlConnection?

我通过 Android SDK 中的默认 HttpClient 将照片上传到服务器。我想在用户界面中显示进度,有没有办法找出已经上传了多少?是否可以使用 HttpUrlConnection?

采纳答案by Ben Groot

For me HTTPClient didn't work. The bytes where buffered in parts and sent as total after the flush call. What worked was to sent it on socket level.

对我来说 HTTPClient 不起作用。在刷新调用后分部分缓冲并作为总数发送的字节。有效的是在套接字级别发送它。

You can use the HttpMultipartClient for this (updated link on 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

您可以为此使用 HttpMultipartClient(更新链接于 30-10-2011):http: //code.google.com/p/rainbowlibs/source/browse/android/trunk/rainbowlibs/src/it/rainbowbreeze/libs/数据/HttpMultipartClient.java?spec=svn94&r=94

Specify the amount of bytes for each part and update the progressbar in the while loop:

指定每个部分的字节数并更新 while 循环中的进度条:

while (( line = reader.readLine()) != null && !headersEnd)

while (( line = reader.readLine()) != null && !headersEnd)

Call the HttpMultipartClient as folow:

调用 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();

At the server side use:

在服务器端使用:

<?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!";
}

?>

I used this for Bluppr Postcards, worked like a charm. If you need more info let me know.

我将它用于 Bluppr 明信片,效果很好。如果您需要更多信息,请告诉我。

回答by TjerkW

1) Be sure to perform your upload in a Service with its own thread.

1) 确保在具有自己线程的服务中执行上传。

2) To get the progress: Wrap your InputStream in this class, and use a a httpmime.jar library which has MultiPart support for HttpClient. I used a thread which checks the progress and updates the progressbar in the notification.

2) 要获得进度:将您的 InputStream 包装在此类中,并使用一个 httpmime.jar 库,该库对 HttpClient 具有 MultiPart 支持。我使用了一个线程来检查进度并更新通知中的进度条。

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

}

回答by Kyle Falconer

I needed the upload progress for an image and was not using the HttpMultipartClient because of implementation issues (trouble getting the package through gradle and dependency errors). Another issue I was running into was getting the actual file size of the image I wanted to upload.

我需要图像的上传进度,但由于实现问题(无法通过 gradle 和依赖项错误获取包)而没有使用 HttpMultipartClient。我遇到的另一个问题是获取我想要上传的图像的实际文件大小。

My requirements also included having the upload in the notification area. Here is my solution:

我的要求还包括在通知区域上传。这是我的解决方案:

Getting the image size

获取图像大小

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 extends AsyncTask<UploadableImage, Integer, String>

AsyncHttpPostTask#onProgressUpdate

AsyncHttpPostTask#onProgressUpdate

This function is called from within AsyncHttpPostTask#doInBackgroundwhich calls the callback to alert the activity of the status change.

这个函数是从内部AsyncHttpPostTask#doInBackground调用的,它调用回调来提醒状态变化的活动。

@Override
protected void onProgressUpdate(Integer... progress) {
    ((ImageUploadActivity) activity).updateProgress(progress[0]);
}

AsyncHttpPostTask#doInBackground

AsyncHttpPostTask#doInBackground

As I mentioned before, I did not use the HttpMultipartClient, so I had to sort-of implement my own. Most of this comes from http://www.androidsnippets.com/multipart-http-requests

正如我之前提到的,我没有使用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

AsyncHttpPostTask#onPostExecute

Here I parse my server's JSON response to see if the upload was able to be processed successfully, then return a message to the Activity which controls the notification.

在这里,我解析服务器的 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);
}

Displaying the progress

显示进度

In the Activity which is responsible for the notification, I have this callback function which is called from the async task. Displaying the progress here can also be done using one of the solutions discussed on John Russell's blog post. This Activity is launched with mode singleTopso that when it is brought to the front with the notification, the state is preserved.

在负责通知的 Activity 中,我有一个从异步任务调用的回调函数。也可以使用John Russell 的博客文章中讨论的解决方案之一在此处显示进度。此 Activity 以 mode 启动,singleTop因此当它与通知一起被带到前面时,状态会被保留。

ImageUploadActivity#buildNotify

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

ImageUploadActivity#updateProgress

This method flushes out the progress to the notification and also to the UI contained within the Activity.

此方法将进度刷新到通知以及包含在 Activity 中的 UI。

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

回答by Puran

Or you should use AsyncTask to do the actual process of file upload and use ProcessDialog to start and stop the process.

或者您应该使用 AsyncTask 来执行文件上传的实际过程,并使用 ProcessDialog 启动和停止该过程。

You can see this code, http://github.com/narup/mymobile/blob/master/pbdroid/src/com/example/android/skeletonapp/StoreListActivity.javai wrote to load the JSON data over HTTP and i use process dialog.

你可以看到这个代码,http://github.com/narup/mymobile/blob/master/pbdroid/src/com/example/android/skeletonapp/StoreListActivity.java我写的通过 HTTP 加载 JSON 数据,我使用进程对话。

Main part of the code is :

代码的主要部分是:

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

}

回答by ud_an

I haven't used httpclient but I have done something like you want using AsyncTask.

我没有使用过 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);          
                }
             }

See in background process I'm incrementing the progressbar and decoding image and in post execution I'm setting the image.

在后台进程中查看我正在增加进度条和解码图像,在执行后我正在设置图像。

回答by alex2k8

I did not work with that API, but notice that HttpClient is not android specific:

我没有使用该 API,但请注意 HttpClient 不是特定于 android 的:

org.apache.http.client.HttpClient

So if you google for "HttpClient progress" there is a number of posts that may be usefull.

因此,如果您在 google 上搜索“HttpClient 进度”,那么有许多帖子可能很有用。

Also, consider that post Android Download Progress

另外,请考虑发布Android 下载进度

回答by John Russell

I wrote up an example of exactly how to do this -> http://toolongdidntread.com/android/android-multipart-post-with-progress-bar/

我写了一个具体如何做到这一点的例子 - > http://toolongdidntread.com/android/android-multipart-post-with-progress-bar/