Ist es möglich, Fortschrittsbalken beim Hochladen von Bildern über Retrofit 2 anzuzeigen

Ich bin derzeit mit Retrofit 2 und ich möchte ein Foto auf meinem Server hochladen. Ich weiß, dass ältere Version die TypedFile Klasse zum Hochladen verwendet. Und wenn wir den Fortschrittsbalken damit nutzen wollen, sollten wir die writeTo Methode in der TypedFile Klasse TypedFile .

Ist es möglich, Fortschritte bei der Verwendung von retrofit 2 Bibliothek zu zeigen?

  • So laden Sie eine Datei in Google Drive hoch
  • Marquee Set Geschwindigkeit
  • So verwenden Sie private Schlüssel aus dem Android-Speicher
  • Android Bluetooth Status 133 in onCharacteristicwrite
  • OpenCV undefinierte Referenzen
  • Android Sqlite Leistung
  • Listenansicht mit CursorAdapter
  • Wie erkennt man Fehler beim Speichern von Bildern im android falschen Dichteverzeichnis?
  • Android genymotion vs emulator
  • Folder Namenskonvention für Gradle-Build-Varianten
  • So entfernen Sie R.java aus JavaDoc
  • Bild drehen Animation Looping funktioniert nicht auf Android
  • 8 Solutions collect form web for “Ist es möglich, Fortschrittsbalken beim Hochladen von Bildern über Retrofit 2 anzuzeigen”

    Zuerst sollten Sie Retrofit 2 Version gleich oder über 2.0 beta2 verwenden. Zweitens erstellen neue Klasse erweitert RequestBody :

      public class ProgressRequestBody extends RequestBody { private File mFile; private String mPath; private UploadCallbacks mListener; private static final int DEFAULT_BUFFER_SIZE = 2048; public interface UploadCallbacks { void onProgressUpdate(int percentage); void onError(); void onFinish(); } public ProgressRequestBody(final File file, final UploadCallbacks listener) { mFile = file; mListener = listener; } @Override public MediaType contentType() { // i want to upload only images return MediaType.parse("image/*"); } @Override public long contentLength() throws IOException { return mFile.length(); } @Override public void writeTo(BufferedSink sink) throws IOException { long fileLength = mFile.length(); byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; FileInputStream in = new FileInputStream(mFile); long uploaded = 0; try { int read; Handler handler = new Handler(Looper.getMainLooper()); while ((read = in.read(buffer)) != -1) { // update progress on UI thread handler.post(new ProgressUpdater(uploaded, fileLength)); uploaded += read; sink.write(buffer, 0, read); } } finally { in.close(); } } private class ProgressUpdater implements Runnable { private long mUploaded; private long mTotal; public ProgressUpdater(long uploaded, long total) { mUploaded = uploaded; mTotal = total; } @Override public void run() { mListener.onProgressUpdate((int)(100 * mUploaded / mTotal)); } } } 

    Drittens schaffen Schnittstelle

     @Multipart @POST("/upload") Call<JsonObject> uploadImage(@Part MultipartBody.Part file); 

    Jetzt kannst du Fortschritt deines Uploads bekommen.

    In deiner activity (oder fragment ):

     class MyActivity extends AppCompatActivity implements ProgressRequestBody.UploadCallbacks { ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); progressBar = findViewById(R.id.progressBar); ProgressRequestBody fileBody = new ProgressRequestBody(file, this); MultipartBody.Part filePart = MultipartBody.Part.createFormData("image", file.getName(), fileBody); Call<JsonObject> request = RetrofitClient.uploadImage(filepart); request.enqueue(new Callback<JsonObject>{...}); } @Override public void onProgressUpdate(int percentage) { // set current progress progressBar.setProgress(percentage); } @Override public void onError() { // do something on error } @Override public void onFinish() { // do something on upload finished // for example start next uploading at queue progressBar.setProgress(100); } } 

    Hier ist, wie man den Upload der Datei Fortschritte mit einem einfachen POST statt Multipart zu behandeln. Für multipart check out @ Yariys Lösung. Darüber hinaus verwendet diese Lösung Content-URIs statt direkter Dateireferenzen.

    RestClient

     @Headers({ "Accept: application/json", "Content-Type: application/octet-stream" }) @POST("api/v1/upload") Call<FileDTO> uploadFile(@Body RequestBody file); 

    ProgressRequestBody

     public class ProgressRequestBody extends RequestBody { private static final String LOG_TAG = ProgressRequestBody.class.getSimpleName(); public interface ProgressCallback { public void onProgress(long progress, long total); } public static class UploadInfo { //Content uri for the file public Uri contentUri; // File size in bytes public long contentLength; } private WeakReference<Context> mContextRef; private UploadInfo mUploadInfo; private ProgressCallback mListener; private static final int UPLOAD_PROGRESS_BUFFER_SIZE = 8192; public ProgressRequestBody(Context context, UploadInfo uploadInfo, ProgressCallback listener) { mContextRef = new WeakReference<>(context); mUploadInfo = uploadInfo; mListener = listener; } @Override public MediaType contentType() { // NOTE: We are posting the upload as binary data so we don't need the true mimeType return MediaType.parse("application/octet-stream"); } @Override public void writeTo(BufferedSink sink) throws IOException { long fileLength = mUploadInfo.contentLength; byte[] buffer = new byte[UPLOAD_PROGRESS_BUFFER_SIZE]; InputStream in = in(); long uploaded = 0; try { int read; while ((read = in.read(buffer)) != -1) { mListener.onProgress(uploaded, fileLength); uploaded += read; sink.write(buffer, 0, read); } } finally { in.close(); } } /** * WARNING: You must override this function and return the file size or you will get errors */ @Override public long contentLength() throws IOException { return mUploadInfo.contentLength; } private InputStream in() throws IOException { InputStream stream = null; try { stream = getContentResolver().openInputStream(mUploadInfo.contentUri); } catch (Exception ex) { Log.e(LOG_TAG, "Error getting input stream for upload", ex); } return stream; } private ContentResolver getContentResolver() { if (mContextRef.get() != null) { return mContextRef.get().getContentResolver(); } return null; } } 

    Um den Upload zu starten:

     // Create a ProgressRequestBody for the file ProgressRequestBody requestBody = new ProgressRequestBody( getContext(), new UploadInfo(myUri, fileSize), new ProgressRequestBody.ProgressCallback() { public void onProgress(long progress, long total) { //Update your progress UI here //You'll probably want to use a handler to run on UI thread } } ); // Upload mRestClient.uploadFile(requestBody); 

    Warnung, wenn Sie vergessen, die contentLength () – Funktion zu überschreiben, erhalten Sie einige obskure Fehler:

     retrofit2.adapter.rxjava.HttpException: HTTP 503 client read error 

    Oder

     Write error: ssl=0xb7e83110: I/O error during system call, Broken pipe 

    Oder

     javax.net.ssl.SSLException: Read error: ssl=0x9524b800: I/O error during system call, Connection reset by peer 

    Dies ist ein Ergebnis von RequestBody.writeTo (), das mehrfach aufgerufen wird, da die Standard-ContentLength () -1 ist.

    Wie auch immer, das hat lange gedauert, um herauszufinden, hoffe es hilft.

    Nützliche Links: https://github.com/square/retrofit/issues/1217

    Ich aktualisiere die progressbar onProgressUpdate. Dieser Code kann bessere Leistung bekommen.

     @Override public void writeTo(BufferedSink sink) throws IOException { long fileLength = mFile.length(); byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; FileInputStream in = new FileInputStream(mFile); long uploaded = 0; try { int read; Handler handler = new Handler(Looper.getMainLooper()); int num = 0; while ((read = in.read(buffer)) != -1) { int progress = (int) (100 * uploaded / fileLength); if( progress > num + 1 ){ // update progress on UI thread handler.post(new ProgressUpdater(uploaded, fileLength)); num = progress; } uploaded += read; sink.write(buffer, 0, read); } } finally { in.close(); } } 

    Geänderte Yuriy Kolbasinskiy's verwenden Rxjava und verwenden Sie kotlin. Ein Workaround für die Verwendung von HttpLoggingInterceptor zur gleichen Zeit hinzugefügt

     class ProgressRequestBody : RequestBody { val mFile: File val ignoreFirstNumberOfWriteToCalls : Int constructor(mFile: File) : super(){ this.mFile = mFile ignoreFirstNumberOfWriteToCalls = 0 } constructor(mFile: File, ignoreFirstNumberOfWriteToCalls : Int) : super(){ this.mFile = mFile this.ignoreFirstNumberOfWriteToCalls = ignoreFirstNumberOfWriteToCalls } var numWriteToCalls = 0 protected val getProgressSubject: PublishSubject<Float> = PublishSubject.create<Float>() fun getProgressSubject(): Observable<Float> { return getProgressSubject } override fun contentType(): MediaType { return MediaType.parse("video/mp4") } @Throws(IOException::class) override fun contentLength(): Long { return mFile.length() } @Throws(IOException::class) override fun writeTo(sink: BufferedSink) { numWriteToCalls++ val fileLength = mFile.length() val buffer = ByteArray(DEFAULT_BUFFER_SIZE) val `in` = FileInputStream(mFile) var uploaded: Long = 0 try { var read: Int read = `in`.read(buffer) while (read != -1) { uploaded += read.toLong() sink.write(buffer, 0, read) read = `in`.read(buffer) // when using HttpLoggingInterceptor it calls writeTo and passes data into a local buffer just for logging purposes. // the second call to write to is the progress we actually want to track if (numWriteToCalls > ignoreFirstNumberOfWriteToCalls ) { val progress = (uploaded.toFloat() / fileLength.toFloat()) * 100f //prevent publishing too many updates, which slows upload, by checking if the upload has progressed by at least 1 percent if (progress - lastProgressPercentUpdate > 1 || progress == 100f) { // publish progress getProgressSubject.onNext(progress) lastProgressPercentUpdate = progress } } } } finally { `in`.close() } } companion object { private val DEFAULT_BUFFER_SIZE = 2048 } 

    }

    Eine Beispiel-Video-Upload-Schnittstelle

     public interface Api { @Multipart @POST("/upload") Observable<ResponseBody> uploadVideo(@Body MultipartBody requestBody); } 

    Eine Beispielfunktion, um ein Video zu posten:

     fun postVideo(){ val api : Api = Retrofit.Builder() .client(OkHttpClient.Builder() //.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) .build()) .baseUrl("BASE_URL") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build() .create(Api::class.java) val videoPart = ProgressRequestBody(File(VIDEO_URI)) //val videoPart = ProgressRequestBody(File(VIDEO_URI), 1) //HttpLoggingInterceptor workaround val requestBody = MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("example[name]", place.providerId) .addFormDataPart("example[video]","video.mp4", videoPart) .build() videoPart.getProgressSubject() .subscribeOn(Schedulers.io()) .subscribe { percentage -> Log.i("PROGRESS", "${percentage}%") } var postSub : Disposable?= null postSub = api.postVideo(requestBody) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ r -> },{e-> e.printStackTrace() postSub?.dispose(); }, { Toast.makeText(this,"Upload SUCCESS!!",Toast.LENGTH_LONG).show() postSub?.dispose(); }) } 

    Soweit ich in diesem Beitrag sehen kann, wurden keine Aktualisierungen bezüglich der Image-Upload-Response-Antwort gemacht und du musst immer noch die writeTo Methode writeTo , wie in dieser SO-Antwort gezeigt, indem du eine ProgressListener Schnittstelle TypedFile und eine Unterklasse von TypedFile override die writeTo Methode.

    So gibt es keinen eingebauten Weg, um Fortschritte bei der Verwendung von Retrofit 2 Bibliothek zu zeigen.

    Ich habe versucht, über Code zu verwenden, aber ich fand UI wurde immer stecken, so dass ich versucht, diesen Code das funktioniert für mich oder kann versuchen, diesen Code verwenden

    @ Luca992 Vielen Dank für Ihre Antwort. Ich habe das in JAVA implementiert und jetzt geht es gut

     public class ProgressRequestBodyObservable extends RequestBody { File file; int ignoreFirstNumberOfWriteToCalls; int numWriteToCalls;`enter code here` public ProgressRequestBodyObservable(File file) { this.file = file; ignoreFirstNumberOfWriteToCalls =0; } public ProgressRequestBodyObservable(File file, int ignoreFirstNumberOfWriteToCalls) { this.file = file; this.ignoreFirstNumberOfWriteToCalls = ignoreFirstNumberOfWriteToCalls; } PublishSubject<Float> floatPublishSubject = PublishSubject.create(); public Observable<Float> getProgressSubject(){ return floatPublishSubject; } @Override public MediaType contentType() { return MediaType.parse("image/*"); } @Override public long contentLength() throws IOException { return file.length(); } @Override public void writeTo(BufferedSink sink) throws IOException { numWriteToCalls++; float fileLength = file.length(); byte[] buffer = new byte[2048]; FileInputStream in = new FileInputStream(file); float uploaded = 0; try { int read; read = in.read(buffer); float lastProgressPercentUpdate = 0; while (read != -1) { uploaded += read; sink.write(buffer, 0, read); read = in.read(buffer); // when using HttpLoggingInterceptor it calls writeTo and passes data into a local buffer just for logging purposes. // the second call to write to is the progress we actually want to track if (numWriteToCalls > ignoreFirstNumberOfWriteToCalls ) { float progress = (uploaded / fileLength) * 100; //prevent publishing too many updates, which slows upload, by checking if the upload has progressed by at least 1 percent if (progress - lastProgressPercentUpdate > 1 || progress == 100f) { // publish progress floatPublishSubject.onNext(progress); lastProgressPercentUpdate = progress; } } } } finally { in.close(); } } } 

    Für das Retrofit 2.0.0-beta4 ist das Hochladen von Dateien nicht korrekt implementiert

    Parser Quellcode für jetzt

     @Documented @Target(PARAMETER) @Retention(RUNTIME) public @interface Part { String value(); String encoding() default "binary"; } // ##### okhttp3.Headers headers = okhttp3.Headers.of( "Content-Disposition", "form-data; name=\"" + part.value() + "\"", "Content-Transfer-Encoding", part.encoding()); 

    Und es gibt keine Möglichkeit, Dateiname durch Annotation hinzuzufügen

    So verwenden wir diesen Hack, um den Dateinamen einzufügen

    Denn jetzt muss die Schnittstelle sein

     @Multipart @POST("some/method") Observable<Response<SomeClass>> UpdateUserPhoto( // RxJava @Part("token") RequestBody token, @Part("avatar\"; filename=\"avatar.png") RequestBody photo ); 

    Und nach dem Erstellen einer Anfrage nehmen wir

     Content-Disposition: form-data; name="avatar"; filename="avatar.png" Content-Transfer-Encoding: binary 

    RequestBody Medientyp für Datei (Bild) muss sein

     MediaType MEDIA_TYPE_IMAGE = MediaType.parse("image/*"); 

    Oder etwas anderes in deiner Option

    Das Android ist ein Google Android Fan-Website, Alles ├╝ber Android Phones, Android Wear, Android Dev und Android Spiele Apps und so weiter.