2016-04-24 13 views
6

Ich versuche, die Open-Source-Bibliothek von Cloudinary zu ändern, so dass ich den Fortschritt des Hochladens meines Fotos hören kann. Die Bibliotheksklasse enthält eine Java-Klasse MultipartUtility, die ich modifiziert habe, um den Fortschritt des Uploads zu überwachen.Android: Upload eines Fotos in Cloudinary mit Fortschritt Rückruf in HttpURLConnection

kann Der ursprüngliche Code vor Änderungen auf Github zu finden: https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java

ich es buchstäblich verändert den Code aus einem anderen Cloud-Dienst CloudFS ähneln, die den Fortschritt unterstützt, wenn Dateien/Bilder etc Hochladen:

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

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

} 

Die Änderungen, die ich vorgenommen habe, sollten dem httpURLConnection wie von diesem Thread empfohlen hinzugefügt werden: How to implement file upload progress bar in android: httpConn.setFixedLengthStreamingMode(filesize);

Ich habe dann eine einfache Schnittstelle für den Upload-Fortschritt zu hören:

public interface UploadingCallback { 

    void uploadListener(int progress); 

} 

Und ich es dann angebracht, während der HttpURLConnection das Foto schrieb:

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

     } 

Der Code lief aber den Fortschritt Der Upload scheint nicht korrekt zu sein. Das Foto war ungefähr 365kb groß und der Upload dauerte ungefähr eine Zehntelsekunde (ich habe den Upload bei 17: 56: 55.481 gestartet und um 17: 56: 55.554 war es fertig, das sind etwas über 0,7 Sekunden). Ich glaube nicht, dass meine Internetverbindung so schnell ist und ich erwarte, dass sie mindestens 5 Sekunden dauert. Ich habe das Gefühl, dass es die Zeit misst, die benötigt wurde, um das Foto in den Puffer zu schreiben statt die Zeit, die es brauchte, um es an die Server von Cloudinary zu senden.

Wie kann ich die Zeit messen, die zum Hochladen des Fotos benötigt wird, damit ich die Daten für meinen Fortschrittsbalken verwenden kann?

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 

diese für sich selbst testen, können Sie ein kostenloses Konto auf cloudinary Website, um Sie zu erhalten erstellen müssen Ihre cloudname so kann auf Ihr Android-SDK, um ihre Dienste für einen unsigned direkten Upload von Android direkt verbinden ihre Server.

EDIT:

Dies ist, was ich versucht habe, und es springt immer noch 0 bis 100% in 0,7 Sekunden, wenn der Upload tatsächlich in 7 Sekunden Zeit beendet:

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

Antwort

3

Es ist eine Problem bei der Verwendung der Flush() -Methode und der Zeit, die Sie Callback() aufrufen.

Wie Sie aus Ihrem Code sehen können, jedes Mal, wenn Sie einen Teil des Bildes lesen, schreiben Sie es in den Ausgabepuffer, aber das bedeutet nicht, dass es an den Server gesendet wird, kann gepuffert werden und später schreiben ' n zum Server.

Sie haben zwei Optionen, entweder outputStream.flush() nach jedem outputStream.write() aufrufen, aber das wird die Leistung des Uploads zu töten, weil Sie die Vorteile der Pufferung verlieren würde.

Oder Sie können Ihre updateCallback() nach der outputStream.flush() am Ende Ihrer Methode aufrufen. Denn nach outputStream.flush() Sie sind sicher, dass die Daten auf dem Server sind, und dieser Fortschritt ist vorbei.

Für weitere Informationen über den Flush sehen diesem Thread What is the purpose of flush() in Java streams?

+0

Ihre zweite Alternative macht sehr wenig Sinn, weil das auch bei 100% und der ganze Zweck wäre das, was ich versuche zu tun, um eine Fortschrittsanzeige für die erhalten hochladen. Ich habe deine erste Alternative ausprobiert und es scheint nicht zu funktionieren. Ich habe eine Änderung eingefügt, um zu zeigen, dass du es ausprobieren willst. – Simon

+0

Gut, als Sie etwas tun müssen, wie die ug__ vorschlagen. Sie sollten flush() gelegentlich aufrufen :), aber wie ich von Ihrer Änderung sehen kann, funktioniert das auch nicht für Sie. Ich muss das testen und Sie wissen lassen, aber ich bin mir zu 99% sicher, dass der Aufruf von flush() der richtige Weg ist. Vielleicht gibt es ein Problem in einem anderen Teil Ihres Codes. Ich muss das testen, was ich gleich machen werde. – aleksamarkoni

+0

Technisch gesehen führt das Aufrufen von flush in outputstream nichts aus, da die Dokumentation sagt, dass sie nichts tut: https://docs.oracle.com/javase/7/docs/api/java/io/OutputStream.html#flush() I ' m um es in DataOutputStream zu werfen, um zu sehen, ob es jetzt einen Unterschied machen wird. Es macht eigentlich keinen Unterschied. – Simon

0

Dies ist ein Schuss im Dunkeln, weil ich nicht auf einem Android-Umgebung getestet haben, jedoch würde ich empfehlen, die folgende versuchen.

Statt setChunkedStreamingMode

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

tun, diese sollte Trigger Teil der Anforderung eine feste Länge Verwendung der Verwendung zu erhalten, jedesmal wenn Sie in 4096 Bytes an Daten senden gesendet und im Wesentlichen den internen Puffer Ausspülen.


Sie auch manuell Spülen des Puffers versuchen könnte nach jeder schreiben, könnte dies vor allem die Datei-Upload verlangsamen, wenn Sie bündig zu oft aber es wahrscheinlich Ihr Problem beheben würde. Sie könnten am Ende mit Puffergrößen spielen, um einen Sweetspot zu finden.

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

Mit jedem dieser Änderungen würden Sie wahrscheinlich der Benutzer wählen, um ihre eigene Puffergröße statt vorbei in der Gesamtgröße der Datei lassen wollen. EG Konstruktor wie folgt ändern:

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

Ich bin ein wenig nervös über die Verwendung von setChunkedStreamingMode, wie ich in den Dokumenten lesen, dass: Old HTTP/1.0 Only Server möglicherweise diesen Modus nicht unterstützen. Ich weiß nicht, welche Art von Server Cloudinary verwendet, so dass es möglicherweise nicht funktioniert. Der Festlängenstrommodus scheint diese Einschränkung nicht zu haben. Wie auch immer, ich habe auch Ihre Flush-Vorschläge versucht und es scheint nicht zu funktionieren. Siehe Bearbeiten in meiner Frage, um zu sehen, was ich versucht habe. – Simon

+0

@Simon Die Dokumentation besagt, dass die URLConnection-Implementierung für RFC 2616 ausgelegt ist, also für HTTP/1.Ich würde sehr überrascht sein, wenn der Server, mit dem Sie sich verbinden möchten, diese Spezifikation nicht unterstützt, da sie heutzutage ein Standard im Web ist. Ich schaue auf den ersten Satz von http://developer.android.com/reference/java/net/HttpURLConnection.html –