2016-04-24 33 views
6

我正在尝试修改cloudinary的开源库,以便我可以听取上传照片的进度。库类包含一个MultipartUtility java类,我修改该类来监听上传的进度。 https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.javaAndroid:在Cloudinary中用HttpURLConnection中的进度回调上传照片

我硬是修改它类似于从另一个云服务CloudFS上传文件/图片等时,支持进度代码:

https://github.com/bitcasa/CloudFS-Android/blob/master/app/src/main/java/com/bitcasa/cloudfs/api/MultipartUpload.java

修改之前,原代码可以在GitHub上找到

package com.cloudinary.android; 

import com.cloudinary.Cloudinary; 

import java.io.*; 
import java.net.HttpURLConnection; 
import java.net.URL; 
import java.util.Map; 

/** 
* This utility class provides an abstraction layer for sending multipart HTTP 
* POST requests to a web server. 
* 
* @author www.codejava.net 
* @author Cloudinary 
*/ 
public class MultipartUtility { 
    private final String boundary; 
    private static final String LINE_FEED = "\r\n"; 
    private static final String APPLICATION_OCTET_STREAM = "application/octet-stream"; 
    private HttpURLConnection httpConn; 
    private String charset; 
    private OutputStream outputStream; 
    private PrintWriter writer; 
    UploadingCallback uploadingCallback; 
    public final static String USER_AGENT = "CloudinaryAndroid/" + Cloudinary.VERSION; 
    Long filesize; 

    public void setUploadingCallback(UploadingCallback uploadingCallback) { 
     this.uploadingCallback = uploadingCallback; 
    } 

    /** 
    * This constructor initializes a new HTTP POST request with content type is 
    * set to multipart/form-data 
    * 
    * @param requestURL 
    * @param charset 
    * @throws IOException 
    */ 
    public MultipartUtility(String requestURL, String charset, String boundary, Map<String, String> headers, Long filesize) throws IOException { 
     this.charset = charset; 
     this.boundary = boundary; 
     this.filesize = filesize; 
     URL url = new URL(requestURL); 
     httpConn = (HttpURLConnection) url.openConnection(); 
     httpConn.setDoOutput(true); // indicates POST method 
     httpConn.setDoInput(true); 
     httpConn.setFixedLengthStreamingMode(filesize); //added this in 

     if (headers != null) { 
      for (Map.Entry<String, String> header : headers.entrySet()) { 
       httpConn.setRequestProperty(header.getKey(), header.getValue()); 
      } 
     } 
     httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); 
     httpConn.setRequestProperty("User-Agent", USER_AGENT); 
     outputStream = httpConn.getOutputStream(); 
     writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true); 
    } 

    public MultipartUtility(String requestURL, String charset, String boundary) throws IOException { 
     this(requestURL, charset, boundary, null, 0L); 
    } 

    /** 
    * Adds a form field to the request 
    * 
    * @param name field name 
    * @param value field value 
    */ 
    public void addFormField(String name, String value) { 
     writer.append("--" + boundary).append(LINE_FEED); 
     writer.append("Content-Disposition: form-data; name=\"" + name + "\"").append(LINE_FEED); 
     writer.append("Content-Type: text/plain; charset=" + charset).append(LINE_FEED); 
     writer.append(LINE_FEED); 
     writer.append(value).append(LINE_FEED); 
     writer.flush(); 
    } 

    /** 
    * Adds a upload file section to the request 
    * 
    * @param fieldName name attribute in {@code <input type="file" name="..." />} 
    * @param uploadFile a File to be uploaded 
    * @throws IOException 
    */ 
    public void addFilePart(String fieldName, File uploadFile, String fileName) throws IOException { 
     if (fileName == null) fileName = uploadFile.getName(); 
     FileInputStream inputStream = new FileInputStream(uploadFile); 
     addFilePart(fieldName, inputStream, fileName); 
    } 

    public void addFilePart(String fieldName, File uploadFile) throws IOException { 
     addFilePart(fieldName, uploadFile, "file"); 
    } 

    public void addFilePart(String fieldName, InputStream inputStream, String fileName) throws IOException { 
     if (fileName == null) fileName = "file"; 
     writer.append("--" + boundary).append(LINE_FEED); 
     writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"").append(LINE_FEED); 
     writer.append("Content-Type: ").append(APPLICATION_OCTET_STREAM).append(LINE_FEED); 
     writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED); 
     writer.append(LINE_FEED); 
     writer.flush(); 

     int progress = 0; 
     byte[] buffer = new byte[4096]; 
     int bytesRead = -1; 

     while ((bytesRead = inputStream.read(buffer)) != -1) { 
      outputStream.write(buffer, 0, bytesRead); 
      progress += bytesRead; 
/*   int percentage = ((progress/filesize.intValue()) * 100);*/ 
      if (uploadingCallback != null) { 
       uploadingCallback.uploadListener(progress); 
      } 

     } 
     outputStream.flush(); 
     writer.flush(); 
     uploadingCallback = null; 
     inputStream.close(); 
     writer.append(LINE_FEED); 
     writer.flush(); 
    } 

    public void addFilePart(String fieldName, InputStream inputStream) throws IOException { 
     addFilePart(fieldName, inputStream, "file"); 
    } 

    /** 
    * Completes the request and receives response from the server. 
    * 
    * @return a list of Strings as response in case the server returned status 
    * OK, otherwise an exception is thrown. 
    * @throws IOException 
    */ 
    public HttpURLConnection execute() throws IOException { 
     writer.append("--" + boundary + "--").append(LINE_FEED); 
     writer.close(); 

     return httpConn; 
    } 

} 

我所做的更改是在本主题推荐的httpURLConnection上添加以下内容:How to implement file upload progress bar in androidhttpConn.setFixedLengthStreamingMode(filesize);

然后,我创建了一个简单的界面来监听上传进度:

public interface UploadingCallback { 

    void uploadListener(int progress); 

} 

然后我重视它,而HttpURLConnection类写道照片:

 while ((bytesRead = inputStream.read(buffer)) != -1) { 
      outputStream.write(buffer, 0, bytesRead); 
      progress += bytesRead; 
/*   int percentage = ((progress/filesize.intValue()) * 100);*/ 
      if (uploadingCallback != null) { 
       uploadingCallback.uploadListener(progress); 
      } 

     } 

的代码运行,但进度的上传似乎没有被正确测量。这张照片大约365kb,上传时间大约为十分之一秒(我开始在17:56:55.481上传,17:56:55.554上传完成,那只是0.7秒)。我不相信我的互联网连接速度很快,并期望它至少需要5秒钟。我有一种感觉,那就是测量将照片写入缓冲区所用的时间,而不是将其发送到cloudinary服务器所用的时间。

我怎样才能测量上传照片所需的时间,以便我可以将数据用于我的进度条?

04-24 17:56:55.481 28306-28725/com.a upload 4096 
04-24 17:56:55.486 28306-28725/com.a upload 8192 
04-24 17:56:55.486 28306-28725/com.a upload 12288 
04-24 17:56:55.486 28306-28725/com.a upload 16384 
04-24 17:56:55.487 28306-28725/com.a upload 20480 
04-24 17:56:55.487 28306-28725/com.a upload 24576 
04-24 17:56:55.487 28306-28725/com.a upload 28672 
04-24 17:56:55.487 28306-28725/com.a upload 32768 
04-24 17:56:55.491 28306-28725/com.a upload 36864 
04-24 17:56:55.492 28306-28725/com.a upload 40960 
04-24 17:56:55.493 28306-28725/com.a upload 45056 
04-24 17:56:55.493 28306-28725/com.a upload 49152 
04-24 17:56:55.493 28306-28725/com.a upload 53248 
04-24 17:56:55.493 28306-28725/com.a upload 57344 
04-24 17:56:55.494 28306-28725/com.a upload 61440 
04-24 17:56:55.494 28306-28725/com.a upload 65536 
04-24 17:56:55.494 28306-28725/com.a upload 69632 
04-24 17:56:55.494 28306-28725/com.a upload 73728 
04-24 17:56:55.494 28306-28725/com.a upload 77824 
04-24 17:56:55.495 28306-28725/com.a upload 81920 
04-24 17:56:55.495 28306-28725/com.a upload 86016 
04-24 17:56:55.495 28306-28725/com.a upload 90112 
04-24 17:56:55.495 28306-28725/com.a upload 94208 
04-24 17:56:55.495 28306-28725/com.a upload 98304 
04-24 17:56:55.495 28306-28725/com.a upload 102400 
04-24 17:56:55.495 28306-28725/com.a upload 106496 
04-24 17:56:55.496 28306-28725/com.a upload 110592 
04-24 17:56:55.496 28306-28725/com.a upload 114688 
04-24 17:56:55.496 28306-28725/com.a upload 118784 
04-24 17:56:55.497 28306-28725/com.a upload 122880 
04-24 17:56:55.498 28306-28725/com.a upload 126976 
04-24 17:56:55.498 28306-28725/com.a upload 131072 
04-24 17:56:55.498 28306-28725/com.a upload 135168 
04-24 17:56:55.498 28306-28725/com.a upload 139264 
04-24 17:56:55.499 28306-28725/com.a upload 143360 
04-24 17:56:55.506 28306-28725/com.a upload 147456 
04-24 17:56:55.510 28306-28725/com.a upload 151552 
04-24 17:56:55.510 28306-28725/com.a upload 155648 
04-24 17:56:55.514 28306-28725/com.a upload 159744 
04-24 17:56:55.515 28306-28725/com.a upload 163840 
04-24 17:56:55.517 28306-28725/com.a upload 167936 
04-24 17:56:55.517 28306-28725/com.a upload 172032 
04-24 17:56:55.518 28306-28725/com.a upload 176128 
04-24 17:56:55.518 28306-28725/com.a upload 180224 
04-24 17:56:55.518 28306-28725/com.a upload 184320 
04-24 17:56:55.519 28306-28725/com.a upload 188416 
04-24 17:56:55.519 28306-28725/com.a upload 192512 
04-24 17:56:55.519 28306-28725/com.a upload 196608 
04-24 17:56:55.519 28306-28725/com.a upload 200704 
04-24 17:56:55.520 28306-28725/com.a upload 204800 
04-24 17:56:55.525 28306-28725/com.a upload 208896 
04-24 17:56:55.526 28306-28725/com.a upload 212992 
04-24 17:56:55.527 28306-28725/com.a upload 217088 
04-24 17:56:55.530 28306-28725/com.a upload 221184 
04-24 17:56:55.530 28306-28725/com.a upload 225280 
04-24 17:56:55.530 28306-28725/com.a upload 229376 
04-24 17:56:55.530 28306-28725/com.a upload 233472 
04-24 17:56:55.530 28306-28725/com.a upload 237568 
04-24 17:56:55.531 28306-28725/com.a upload 241664 
04-24 17:56:55.532 28306-28725/com.a upload 245760 
04-24 17:56:55.532 28306-28725/com.a upload 249856 
04-24 17:56:55.532 28306-28725/com.a upload 253952 
04-24 17:56:55.533 28306-28725/com.a upload 258048 
04-24 17:56:55.533 28306-28725/com.a upload 262144 
04-24 17:56:55.535 28306-28725/com.a upload 266240 
04-24 17:56:55.540 28306-28725/com.a upload 270336 
04-24 17:56:55.540 28306-28725/com.a upload 274432 
04-24 17:56:55.541 28306-28725/com.a upload 278528 
04-24 17:56:55.541 28306-28725/com.a upload 282624 
04-24 17:56:55.543 28306-28725/com.a upload 286720 
04-24 17:56:55.545 28306-28725/com.a upload 290816 
04-24 17:56:55.545 28306-28725/com.a upload 294912 
04-24 17:56:55.547 28306-28725/com.a upload 299008 
04-24 17:56:55.547 28306-28725/com.a upload 303104 
04-24 17:56:55.547 28306-28725/com.a upload 307200 
04-24 17:56:55.547 28306-28725/com.a upload 311296 
04-24 17:56:55.547 28306-28725/com.a upload 315392 
04-24 17:56:55.548 28306-28725/com.a upload 319488 
04-24 17:56:55.548 28306-28725/com.a upload 323584 
04-24 17:56:55.548 28306-28725/com.a upload 327680 
04-24 17:56:55.548 28306-28725/com.a upload 331776 
04-24 17:56:55.549 28306-28725/com.a upload 335872 
04-24 17:56:55.549 28306-28725/com.a upload 339968 
04-24 17:56:55.549 28306-28725/com.a upload 344064 
04-24 17:56:55.550 28306-28725/com.a upload 348160 
04-24 17:56:55.550 28306-28725/com.a upload 352256 
04-24 17:56:55.551 28306-28725/com.a upload 356352 
04-24 17:56:55.551 28306-28725/com.a upload 360448 
04-24 17:56:55.552 28306-28725/com.a upload 364544 
04-24 17:56:55.554 28306-28725/com.a upload 365790 

为了测试这一点为自己,你将需要创建cloudinary网站免费帐户,以获得您的cloudname这样你就可以在Android SDK直接连接到他们的服务从Android的无符号直接上传到他们的服务器

编辑:

这是我曾尝试,它仍然从0跳到 - 100%0.7秒时上传在7秒内的时间实际上完成:

while ((bytesRead = inputStream.read(buffer)) != -1) { 
     outputStream.write(buffer, 0, bytesRead); 
     progress += bytesRead; 
     Log.d("MultiPart", "file transferred so far: " 
       + progress); 
     if (uploadingCallback != null) { 
      uploadingCallback.uploadListener(progress); 
     } 
     Log.d("Flushing", "flush the writer"); 
     outputStream.flush(); 
     writer.flush(); 
    } 

回答

3

有一个使用flush()方法的问题以及调用update callback()的时间。你可以从你的代码中看到,每当你读到图片的一部分时,你就会把它写到输出缓冲区,但这并不意味着它被发送到服务器,它可能会被缓冲,然后再编写' n到服务器。

你有两个选择,要么在每个outputStream.write()之后调用outputStream.flush(),但这会杀死上传的性能,因为你会失去缓冲的好处。

或者您可以在方法结尾处的outputStream.flush()之后调用updateCallback()。因为在outputStream之后。flush()你确定数据在服务器上,并且进度已经结束。

有关冲洗更多信息请参阅本主题What is the purpose of flush() in Java streams?

+0

你的第二个选择让人很没有意义,因为这也将是在100%,而我试图做的是整个目的得到了一个进度指示器上传。我尝试了你的第一个选择,它似乎并没有工作,我已经放入一个编辑,以显示你想我已经尝试过。 – Simon

+0

比你需要做一些类似于ug__的建议。你应该在一段时间内调用flush():),但正如我从你的改变中看到的那样,即使这对你没有任何作用。我需要测试这个,并让你知道,但我99%确定调用flush()是正确的路要走。也许你的代码的其他部分存在问题。将需要测试这个,我会马上去做。 – aleksamarkoni

+0

技术上,呼吁为OutputStream齐平,该文档称,它什么事也不会做任何事情:https://docs.oracle.com/javase/7/docs/api/java/io/OutputStream.html#flush()我将其转换为DataOutputStream以查看它现在是否会有所作为。它实际上没有区别。 – Simon

0

这是一个在黑暗中拍摄,因为我还没有在Android环境中测试,但我会建议您尝试以下。

不是采用固定长度的使用setChunkedStreamingMode

//httpConn.setFixedLengthStreamingMode(filesize); 
httpConn.setChunkedStreamingMode(4096); // or whatever size you see fit 

做请求的这个应该触发部分以获取发送每次你在4096个字节的数据,基本上冲洗掉内部发送缓冲区的时间。


您也可以尝试手动刷新缓冲区每次写入后,这可能尤其是减慢的文件上传,如果你刷新经常但是它可能会解决您的问题。您最终可能会使用缓冲区大小来找到最佳位置。

while ((bytesRead = inputStream.read(buffer)) != -1) { 
    outputStream.write(buffer, 0, bytesRead); 
    progress += bytesRead; 
    /* int percentage = ((progress/filesize.intValue()) * 100);*/ 
    if (uploadingCallback != null) { 
     uploadingCallback.uploadListener(progress); 
    } 
    // trigger the stream to write its data 
    outputStream.flush(); 
} 

随着要么你很可能希望让用户选择设置的,而不是通过在文件总大小自己的缓冲区大小这些变化。 EG改变你的构造函数如下:

MultipartUtility(String requestURL, String charset, 
       String boundary, Map<String, String> headers, int chunkSize) 
+0

我在使用setChunkedStreamingMode时感到有点紧张,因为我在文档中读到:旧的HTTP/1.0服务器可能不支持此模式。我不知道什么类型的服务器cloudinary正在使用,所以它可能无法正常工作。固定长度的数据流模式似乎没有这种限制。无论如何,我也尝试了你的同花顺建议,它似乎并没有工作。请参阅我的问题中的编辑以查看我所尝试的内容。 – Simon

+0

@Simon该文档指出URLConnection实现是为HTTP/1的RFC 2616设计的。1规范,如果您尝试连接的服务器不支持该规范,那么我将非常惊讶,因为它现在是网络上的标准。我在看第一句http://developer.android.com/reference/java/net/HttpURLConnection.html –