安装Azure CloudBlockBlob上传的Android跟踪进度

时间:2017-04-16 20:45:40

标签: java android azure-mobile-services azure-storage-blobs

如何打印调用blob.upload(new FileInputStream(imageFile), imageFile.length());后上传的字节数我想记录类似" 100 / totalBytes字节已上传,224 / totalBytes字节已上传... "所以我可以创建上传进度的进度条。

这是代码:

// 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


}

}

2 个答案:

答案 0 :(得分:2)

正如您所看到的,Azure SDK并不直接允许这样做,但是将输入流包装在另一个可以为读取的字节提供回调的输入流中应该相当容易。这样的事情:

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应该使用一些合理的数字进行初始化,因为您可能不希望每个字节都被调用,也许每半个兆字节应该是好的。

请记住,这一切都会在doInBackground主题上调用,因此请采取相应措施。

修改

我已经编辑了上面的课程,计算bytesRead值时出现了一个小错误。 官方文档解释了您的后续问题https://developer.android.com/reference/java/io/InputStream.html#read()

  

从输入流中读取下一个数据字节

所以如果到达结尾,read()读取1个字节的数据(或返回-1)。所以是的,必须多次调用才能读取整个图像。

然后每次至少onRead(long)读取时调用方法minimumBytesPerCall(为了避免每个字节都回调一次),再次在流的结尾(当它返回-1时)

传递给onRead(long)的值是自上次调用以来已读取的数量。因此,在AsyncTask上实现此功能,您必须累积此值并与文件的总大小进行比较。

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

并在您的上传部分内部替换为:

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

并且作为最后一句话,请记住CloudBlockBlob内部只是将文件缓存在自己的内存中以便以后上传,或者做任何其他不受控制的怪异内容。所有这些代码都检查是否已读取完整文件。

快乐的编码!

答案 1 :(得分:0)

只是满足您需求的另一种方式,有一个MS博客介绍了uploading a blob to Azure Storage with progress bar and variable upload block size。该代码是用C#编写的,但是Java / Android Developer的阅读非常简单,我认为您可以轻松地在Java for Android中重写它,以计算通过一些公共变量共享的上传流程栏比率。

希望它有所帮助。