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

前端 未结 7 592
一向
一向 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: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

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

提交回复
热议问题