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

前端 未结 7 585
一向
一向 2020-12-07 14:51

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?

相关标签:
7条回答
  • 2020-12-07 15:13

    I haven't used httpclient but I have done something like you want using 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.

    0 讨论(0)
  • 2020-12-07 15:14

    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.

    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

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

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

    Call the HttpMultipartClient as folow:

    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.

    0 讨论(0)
  • 2020-12-07 15:21

    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.

    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#onProgressUpdate

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

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

    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

    @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

    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.

    @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 singleTop so that when it is brought to the front with the notification, the state is preserved.

    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

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

    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();
        }
    }
    
    0 讨论(0)
  • 2020-12-07 15:21

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

    org.apache.http.client.HttpClient
    

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

    Also, consider that post Android Download Progress

    0 讨论(0)
  • 2020-12-07 15:25

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

    0 讨论(0)
  • 2020-12-07 15:26

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

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

    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);
    }
    
    }
    
    0 讨论(0)
提交回复
热议问题