在 Android 中實作 Azure Blob Storage 上傳進度條

承上篇,在 iOS 中實作 Azure Blob Storage 上傳進度條 解決了在 iOS 上的日常問題,那當然是 Android 也得來上一發,否則豈不是偏心了。

Java 的解法受到 OOP 的啟發,可以很漂亮又有爽度的解決問題,不然學 OOP 是要什麼時候用呢。

首先看一個在 gist 神人的解決方案,他的思路在於使用 Decorator pattern 與 InputStream 搭配,天衣無縫的讓 Java 程式碼直接支援 Azure Blob Storage 的上傳進度取得。

Main.java 是上傳檔案的主要程式,它的重點在於引用了 WatchingInputStream 類別:

package com.cloudriches.sample;

import com.microsoft.azure.storage.CloudStorageAccount;
import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.CloudBlockBlob;

import java.io.File;
import java.io.FileInputStream;

public class Main implements WatchingInputStream.ProgressListener {

    public static void main(String[] args) throws Exception {
        Main main = new Main();
        main.upload();
    }

    public void upload() throws Exception {
        CloudStorageAccount account = CloudStorageAccount.parse("DefaultEndpointsProtocol=https;AccountName=(your account name);AccountKey=(your account key);EndpointSuffix=core.windows.net");
        CloudBlobClient client = account.createCloudBlobClient();
        CloudBlobContainer container = client.getContainerReference("files");
        CloudBlockBlob blob = container.getBlockBlobReference("A.mp4");
    
        File sourceFile = new File("/Users/dino/A.mp4");
        FileInputStream inputStream = new FileInputStream(sourceFile);
        WatchingInputStream watchingInputStream = new WatchingInputStream(inputStream, this);

        blob.upload(watchingInputStream, sourceFile.length());
    }

    @Override
    public void onAdvance(long at, long length) {
        double percentage = (double)at / (double)length;
        System.out.println(percentage);
    }
}

 WatchInputStream.java 的職責除了原 InputStream 的所有職責,外加上取得檔案已讀數量並計算讀取 %,呼叫 onAdvance 事件:

package com.cloudriches.sample;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

// from https://gist.github.com/alterakey/1454764
public class WatchingInputStream extends FilterInputStream {
    public interface ProgressListener {
        void onAdvance(long at, long length);
    }

    private int marked = 0;
    private long position = 0;
    private ProgressListener listener;

    public WatchingInputStream(InputStream in, ProgressListener listener) {
        super(in);
        this.listener = listener;
    }

    @Override
    public int read(byte[] buffer, int offset, int count) throws IOException {
        int advanced = super.read(buffer, offset, count);
        this.position += advanced;
        this.report();
        return advanced;
    }

    @Override
    public synchronized void reset() throws IOException {
        super.reset();
        this.position = this.marked;
    }

    @Override
    public synchronized void mark(int readlimit) {
        super.mark(readlimit);
        this.marked = readlimit;
    }

    @Override
    public long skip(long byteCount) throws IOException {
        long advanced = super.skip(byteCount);
        this.position += advanced;
        this.report();
        return advanced;
    }

    private void report() {
        if (this.listener == null)
            return;

        try {
            this.listener.onAdvance(this.position, this.position + this.in.available());
        }
        catch (IOException e) {
            this.listener.onAdvance(this.position, 0);
        }
    }
}