Android 跟踪 Azure CloudBlockBlob 上传的进度
Android Track progress of Azure CloudBlockBlob upload
如何打印调用后已上传的字节数 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
}
}
如您所见,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
线程上调用的,因此请相应地采取行动。
编辑:
我已经编辑了上面的 class,在计算 bytesRead
值时出现了一个小错误。
官方文档解释了你的后续问题https://developer.android.com/reference/java/io/InputStream.html#read()
Reads the next byte of data from the input stream
所以如果到达末尾,read()
读取 1 个字节的数据(或 return -1)。所以是的,必须多次调用它才能读取整个图像。
然后方法 onRead(long)
get 在每次至少 minimumBytesPerCall
被读取时调用(这是为了避免为每个字节回调)并在流的末尾再次调用(当它 returns -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
只是将文件缓存在自己的内存中以供以后上传,或者执行您无法控制的任何其他奇怪的事情。这段代码所做的只是检查是否读取了完整的文件。
编码愉快!
另一种方式满足您的需求,有一个 MS 博客介绍了大约 uploading a blob to Azure Storage with progress bar and variable upload block size。该代码是用 C# 编写的,但对于 Java/Android 开发人员阅读起来非常简单,我认为您可以轻松地在 Java 中重写它,以便 Android 计算上传进度条比率,以便通过一些分享public 个变量。
希望对您有所帮助。
如何打印调用后已上传的字节数 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
}
}
如您所见,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
线程上调用的,因此请相应地采取行动。
编辑:
我已经编辑了上面的 class,在计算 bytesRead
值时出现了一个小错误。
官方文档解释了你的后续问题https://developer.android.com/reference/java/io/InputStream.html#read()
Reads the next byte of data from the input stream
所以如果到达末尾,read()
读取 1 个字节的数据(或 return -1)。所以是的,必须多次调用它才能读取整个图像。
然后方法 onRead(long)
get 在每次至少 minimumBytesPerCall
被读取时调用(这是为了避免为每个字节回调)并在流的末尾再次调用(当它 returns -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
只是将文件缓存在自己的内存中以供以后上传,或者执行您无法控制的任何其他奇怪的事情。这段代码所做的只是检查是否读取了完整的文件。
编码愉快!
另一种方式满足您的需求,有一个 MS 博客介绍了大约 uploading a blob to Azure Storage with progress bar and variable upload block size。该代码是用 C# 编写的,但对于 Java/Android 开发人员阅读起来非常简单,我认为您可以轻松地在 Java 中重写它,以便 Android 计算上传进度条比率,以便通过一些分享public 个变量。
希望对您有所帮助。