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

时间:2010-01-20 22:05:57

标签: android

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

7 个答案:

答案 0 :(得分:14)

对我来说,HTTPClient无效。缓冲部分中缓冲的字节,并在刷新调用后作为总计发送。什么工作是在套接字级别发送它。

您可以使用HttpMultipartClient(2011年10月30日更新的链接): 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明信片,就像一个魅力。如果您需要更多信息,请告诉我们。

答案 1 :(得分:3)

1)请务必使用自己的主题在服务中执行上传。

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

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 :(得分:2)

我需要图像的上传进度,并且因为实现问题而无法使用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响应以查看上载是否能够成功处理,然后向控制通知的活动返回一条消息。

@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中,我有一个从异步任务调用的回调函数。此处显示进度也可以使用John Russell's blog post中讨论的解决方案之一完成。此活动以模式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();
    }
}

答案 3 :(得分:1)

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

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

代码的主要部分是:

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

}

答案 4 :(得分:0)

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

org.apache.http.client.HttpClient

因此,如果你谷歌搜索“HttpClient进展”,那么有很多帖子可能会有用。

另外,请考虑发布Android Download Progress

答案 5 :(得分:0)

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

在后台进程中看到我正在递增进度条并解码图像,在后期执行中我正在设置图像。

答案 6 :(得分:0)

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

相关问题