简体   繁体   English

Android跟踪Azure CloudBlockBlob上传的进度

[英]Android Track progress of Azure CloudBlockBlob upload

How can I print the number of bytes that have been uploaded after calling blob.upload(new FileInputStream(imageFile), imageFile.length()); 在调用blob.upload(new FileInputStream(imageFile), imageFile.length());之后,如何打印已上传的字节数blob.upload(new FileInputStream(imageFile), imageFile.length()); I want to log something like "100/totalBytes bytes have been uploaded, 224/totalBytes bytes have been uploaded..." So I can create a progress bar of the upload progress. 我想记录类似“已上传100 / totalBytes字节,已上传224 / totalBytes字节...”这样的内容,因此我可以创建上传进度的进度条。

this is the code: 这是代码:

//AzureBlobLoader extends AsyncTask // AzureBlobLoader扩展了AsyncTask

public class AzureBlobUploader extends AzureBlobLoader {
private Activity act;
private String userName;
private TaggedImageObject img;
private Fragment histFragment;

public AzureBlobUploader(Fragment f, Activity act, String userName, TaggedImageObject img) {
    super();
    this.act = act;
    this.userName = userName;
    this.img = img;
    this.histFragment = f;
}


@Override
protected Object doInBackground(Object[] params) {

    File imageFile = new File(this.img.getImgPath());

    try {

        // Define the path to a local file.
        final String filePath = imageFile.getPath();

        // Create or overwrite the blob with contents from the local file.
        String[] imagePathArray = filePath.split("/");
        String imageName = imagePathArray[imagePathArray.length-1];

        System.out.println("Image Name: " + imageName);

        String containerName = userName + "/" + imageName;

        System.out.println("Container Name: " + containerName);

        CloudBlockBlob blob= this.getContainer().getBlockBlobReference(containerName);


        //UPLOAD!
       blob.upload(new FileInputStream(imageFile), imageFile.length());

        //-----DATABASE-----//
        //create client
        this.setDBClient(
                new MobileServiceClient(
                        "URL",
                        this.act.getApplicationContext()
                )
        );

        this.setImageTable(this.getDBClient().getTable(Image.class));
        this.setIcavTable(this.getDBClient().getTable(ICAV.class));

        //IMG TABLE QUERY
        String validImageID = containerName.replace("/", "_");
        Log.d("Azure", "Valid Image ID: " + validImageID);

        Image img = new Image(validImageID, this.img.getUser(), this.img.getLat(), this.img.getLon());
        this.getImageTable().insert(img);

        for(String context : this.img.getContextAttributeMap().keySet()){
            Map<String,String> attributeValueMap = this.img.getContextAttributeMap().get(context);

            for(String attribute : attributeValueMap.keySet()){
                String value = attributeValueMap.get(attribute);


                ICAV icavRow = new ICAV();
                icavRow.setImageID(validImageID);
                icavRow.setContextID(context);
                icavRow.setAttributeID(attribute);
                icavRow.setValue(value);

                this.getIcavTable().insert(icavRow);
            }

        }
    } catch (Exception e) {
        System.out.println(e.toString());
    }

    return null;
}



@Override
protected void onProgressUpdate(Object... object) {
    super.onProgressUpdate(object);
    Log.d("progressUpdate", "progress: "+((Integer)object[0] * 2) + "%");
}

@Override
protected void onPostExecute(Object o) {
    // to do


}

} }

As you can see the Azure SDK doesn't directly allow for that, but it should be fairly easy to wrap your inputstream in another input stream that can give callbacks for bytes read. 如您所见,Azure SDK不允许直接这样做,但是将您的输入流包装到另一个输入流中应该可以非常容易,该输入流可以为读取的字节提供回调。 Something like that: 像这样:

public class ListenableInputStream extends InputStream {

    private final InputStream wraped;
    private final ReadListener listener;
    private final long minimumBytesPerCall;
    private long bytesRead;

    public ListenableInputStream(InputStream wraped, ReadListener listener, int minimumBytesPerCall) {
        this.wraped = wraped;
        this.listener = listener;
        this.minimumBytesPerCall = minimumBytesPerCall;
    }


    @Override
    public int read() throws IOException {
        int read = wraped.read();
        if (read >= 0) {
            bytesRead++;
        }
        if (bytesRead > minimumBytesPerCall || read == -1) {
            listener.onRead(bytesRead);
            bytesRead = 0;
        }
        return read;
    }

    @Override
    public int available() throws IOException {
        return wraped.available();
    }

    @Override
    public void close() throws IOException {
        wraped.close();
    }

    @Override
    public synchronized void mark(int readlimit) {
        wraped.mark(readlimit);
    }

    @Override
    public synchronized void reset() throws IOException {
        wraped.reset();
    }

    @Override
    public boolean markSupported() {
        return wraped.markSupported();
    }

    interface ReadListener {
        void onRead(long bytes);
    }
}

minimumBytesPerCall should be initialised with some sensible number, as you probably don't want to be called on every single byte, maybe every half a megabyte should be good. minimumBytesPerCall应该用一个合理的数字初始化,因为您可能不想在每个字节上都调用它,也许每半兆字节就可以了。

And remember that this all gets called on the doInBackground thread, so act accordingly. 并记住,所有这些都在doInBackground线程上被调用,因此请采取相应措施。

edit: 编辑:

I've edited the class above, there was a small error on computing the bytesRead value. 我已经编辑了上面的类,在计算bytesRead值时有一个小错误。 The official documentation explains your follow-up questions https://developer.android.com/reference/java/io/InputStream.html#read() 官方文档解释了您的后续问题https://developer.android.com/reference/java/io/InputStream.html#read()

Reads the next byte of data from the input stream 从输入流中读取下一个数据字节

So read() reads 1 byte of data (or return -1) if reached the end. 因此,如果到达末尾, read()会读取1个字节的数据(或返回-1)。 So yes, it must be called several several times to read a whole image. 所以是的,必须多次调用它才能读取整个图像。

Then the method onRead(long) get's called every time at least minimumBytesPerCall have been read (that's to avoid of calling back for every single byte) and once more at the end of the stream (when it returns -1) 然后,每次至少读取了minimumBytesPerCall时,就会调用onRead(long)方法(这是为了避免为每个单个字节回调),并在流的末尾再次调用该方法(返回-1时)

The value passed to onRead(long) is the amount that have been read since the last call. 传递给onRead(long)的值是自上次调用以来已读取的数量。 So implementing this on your AsyncTask you would have to accumulate this value and compare with the total size of the file. 因此,在您的AsyncTask上实现此功能,您将必须累积该值并将其与文件的总大小进行比较。

Something like the following code inside your asynctask should work fine (assuming the Progress generic parameter is a Long): asynctask中的以下代码应可以正常工作(假设Progress通用参数为Long):

    private long fileLength;
    private long totalBytes;

    private final ListenableInputStream.ReadListener readListener = new ListenableInputStream.ReadListener() {
        @Override
        public void onRead(long bytes) {
            totalBytes += bytes;
            publishProgress(totalBytes);
        }
    };

and on inside your upload part you replace with: 并在您的上传部分中替换为:

FileInputStream fis = new FileInputStream(imageFile);
fileLength = imageFile.length();
ListenableInputStream lis = new ListenableInputStream(fi, readListener, 256 * 1024); // this will call onRead(long) every 256kb
blob.upload(lis, fileLength);

and as a last remark, remember that internally the CloudBlockBlob just caching the file on its own memory for later upload, or doing any other weird stuff that is out of your control. 最后,请记住,在内部, CloudBlockBlob只是将文件缓存在其自己的内存中以供以后上载,或者执行其他无法控制的奇怪操作。 All this code does is check that the complete file was read. 这些代码所做的只是检查是否已读取完整文件。

happy coding! 祝您编码愉快!

Just another way for your needs, there is a MS blog which introduce about uploading a blob to Azure Storage with progress bar and variable upload block size . 满足您需求的另一种方式是,有一个MS博客,其中介绍了如何使用进度条和可变的上传块大小将blob上传到Azure存储 That code was written in C#, but it's very simple for reading by Java/Android Developer, I think you can easily rewrite it in Java for Android to compute the uploading processbar ratio to share via some public variables. 该代码是用C#编写的,但是对于Java / Android Developer来说,读取它非常简单,我认为您可以轻松地用Java for Android对其进行重写,以计算上载进程的比率,以通过一些公共变量进行共享。

Hope it helps. 希望能帮助到你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM