通过Retrofit 2上传图像时是否可以显示进度栏?


75

Retrofit 2我当前正在使用,我想在服务器上上传一些照片。我知道,较旧的版本使用TypedFile类进行上传。如果要使用进度条,则应覆盖中的writeTo方法TypedFile类中的。

使用retrofit 2库时可以显示进度吗?


对于仍然停留在Retrofit 1.x上的用户,这是我们最终使用的一种简单,有效的解决方案:stackoverflow.com/a/24772058/293280
Joshua Pinter

Answers:


169

首先,您应该使用等于或高于2.0 beta2的Retrofit 2版本。其次,创建新类扩展RequestBody

public class ProgressRequestBody extends RequestBody {
    private File mFile;
    private String mPath;
    private UploadCallbacks mListener;
    private String content_type;

  private static final int DEFAULT_BUFFER_SIZE = 2048;

    public interface UploadCallbacks {
        void onProgressUpdate(int percentage);
        void onError();
        void onFinish();
    }

请注意,我添加了内容类型,以便除了图像之外还可以容纳其他类型

public ProgressRequestBody(final File file, String content_type,  final  UploadCallbacks listener) {
    this.content_type = content_type;
    mFile = file;
    mListener = listener;            
}



@Override
    public MediaType contentType() {
        return MediaType.parse(content_type+"/*");
    }

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

三,创建界面

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

/ *上面的JsonObject可以用您自己的模型替换,只需要使其显眼即可。* /

现在,您可以获取上传进度。在您activity(或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 onResponse(Call<JsonObject> call,   Response<JsonObject> response) {
                if(response.isSuccessful()){
                /* Here we can equally assume the file has been downloaded successfully because for some reasons the onFinish method might not be called, I have tested it myself and it really not consistent, but the onProgressUpdate is efficient and we can use that to update our progress on the UIThread, and we can then set our progress to 100% right here because the file already downloaded finish. */
                  }
            }

            @Override
            public void onFailure(Call<JsonObject> call, Throwable t) {
                      /* we can also stop our progress update here, although I have not check if the onError is being called when the file could not be downloaded, so I will just use this as a backup plan just in case the onError did not get called. So I can stop the progress right here. */
            }
        });

      }

        @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 a queue
            progressBar.setProgress(100);
        }

}

14
我几乎可以立即看到数据写入接收器,但是在慢速的连接上,实际的调用需要花费几秒钟的时间。我试过在写入后将sink.flush()添加到调用中,但是它似乎仍显示数据内部传输的时间,而不是通过网络传输的进度。
杰克·霍尔

6
handler.post(new ProgressUpdater(uploaded,fileLength)); 该行应在while循环的底部,否则您将始终收到99%。
codezjx

9
我也面临着与JakeHall相同的问题,找不到任何解决方案... writeTo方法被调用两次,并且第一次立即加载,但是第二次是我们寻求的真正上载时间。您对此@YuriyKolbasinskiy有何评论?
yahya's

23
@yahya我看到的是同一件事,发现它是由我设置的HttpLoggingInterceptor引起的。首先,快速加载是HttpLoggingInterceptor调用writeTo并仅出于记录目的传入本地缓冲区。
Devin Pitcher

4
在单个请求中上传多个文件时,如何显示MultipartBody.Part []的进度?
Usman Rana

24

将Yuriy Kolbasinskiy修改为使用rxjava和kotlin。添加了同时使用HttpLoggingInterceptor的解决方法

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
            var lastProgressPercentUpdate = 0.0f
            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
    }
}

视频上传界面示例

public interface Api {

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

发布视频的示例函数:

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

1
这只是显示写入速度而不是上传速度,正在进行中似乎我们已将for循环的进度从0更新为100,因为我的上传不如进度所示快
Akash Dubey

1
@AkashDubey听起来像您正在使用HttpLoggingInterceptor,但未使用我提供的注释掉的解决方法
luca992 '19

抱歉,我的文件太小,因此上载速度更快,它的工作正常
Akash Dubey

1
我也面临着@AkashDubey指出的相同问题。使用HttpLoggingInterceptor的变通办法没有帮助。它只是防止writeTo逻辑执行两次,并且仍然在下一个writeTo调用中立即将数据写入接收器。
Vikalp '20年

DEFAULT_BUFFER_SIZE是什么意思?它是允许的最大文件大小吗?
Alexa289 '20

11

以下是通过简单的POST而非Multipart处理上传文件进度的方法。对于多部分,请查看@Yariy的解决方案。此外,此解决方案使用内容URI代替直接文件引用。

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

要启动上传:

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

警告,如果您忘记覆盖contentLength()函数,则可能会收到一些晦涩的错误:

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

要么

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

要么

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

这是由于默认contentLength()为-1多次调用RequestBody.writeTo()的结果。

无论如何,这花了很长时间才弄清楚,希望对您有所帮助。

有用的链接:https : //github.com/square/retrofit/issues/1217


1
RequestBody.writeTo()被多次调用。你解决了这个吗?因为我要面对这个。打了两次电话!
android_griezmann

@android_griezmann检查您是否设置了正确的contentLength。通过Cursor和OpenableColumns.FILE_SIZE返回的文件大小存在问题,请尝试按此处所述使用ParcelFileDescriptor.getStatSize()代替:stackoverflow.com/questions/21882322/…–
Justin Fiedler

因为您使用拦截器
zihadrizkyef

3

@ luca992谢谢您的回答。我已经在JAVA中实现了此功能,现在一切正常。

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

    }
}

谢谢,如何显示主要上传活动的进度plss ??
Ibramazin

2

我更新进度条onProgressUpdate。此代码可以获得更好的性能。

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

你今天救了我 非常感谢
Gulnaz Ghanchi



1

为了避免两次运行问题。我们可以首先将标志设置为零,并在第一次调用进度对话框后将标志设置为一。

 @Override
    public void writeTo(BufferedSink sink) throws IOException {

        Source source = null;
        try {
            source = Okio.source(mFile);
            total = 0;
            long read;

            Handler handler = new Handler(Looper.getMainLooper());

            while ((read = source.read(sink.buffer(), DEFAULT_BUFFER_SIZE)) != -1) {

                total += read;
                sink.flush();

                // flag for avoiding first progress bar .
                if (flag != 0) {
                    handler.post(() -> mListener.onProgressUpdate((int) (100 * total / mFile.length())));

                }
            }

            flag = 1;

        } finally {
            Util.closeQuietly(source);
        }
    }

0

至于我能看到这个帖子,已经作出关于图像上传进度响应没有更新,你还是要overridewriteTo方法,如在通过使SO答案ProgressListener接口,并使用一个子类的TypedFileoverridewriteTo方法。

因此,在使用翻新2库时,没有任何内置的方式来显示进度。


是的,但是TypedFiled新的
翻新

对。我刚刚检查了一下。您可以RequestBody按照stackoverflow.com/questions/32856850/…中所述使用类。但这并不能解决您确实获得上传进度的问题。
Shubham A.

我在RequestBody课堂上找到了一个解决方案:)
Yuriy Kolbasinskiy

0

此答案用于MultipartBody并上传多个文件。我的服务器端代码是mvc开发。首先,您需要这样的ApiService类:

public interface ApiService {

@POST("Home/UploadVideos")
Call<ResponseBody> postMeme(@Body RequestBody files);
}

并且您需要这样的Apiclient:

public class ApiClient {
public static final String API_BASE_URL = "http://192.168.43.243/Web/";

private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();

private static Retrofit.Builder builder = new Retrofit.Builder().baseUrl(API_BASE_URL).addConverterFactory(GsonConverterFactory.create());

public static ApiService createService(Class<ApiService> serviceClass)
{
    Retrofit retrofit = builder.client(httpClient.build()).build();
    return retrofit.create(serviceClass);
}
}

之后,您需要像这样的ReqestBody类:

public class CountingFileRequestBody extends RequestBody {
private static final String TAG = "CountingFileRequestBody";

private final ProgressListener listener;
private final String key;
private final MultipartBody multipartBody;
protected CountingSink mCountingSink;

public CountingFileRequestBody(MultipartBody multipartBody,
                               String key,
                               ProgressListener listener) {
    this.multipartBody = multipartBody;
    this.listener = listener;
    this.key = key;
}

@Override
public long contentLength() throws IOException {
    return multipartBody.contentLength();
}

@Override
public MediaType contentType() {
    return multipartBody.contentType();
}

@Override
public void writeTo(BufferedSink sink) throws IOException {
    mCountingSink = new CountingSink(sink);
    BufferedSink bufferedSink = Okio.buffer(mCountingSink);
    multipartBody.writeTo(bufferedSink);
    bufferedSink.flush();
}

public interface ProgressListener {
    void transferred(String key, int num);
}

protected final class CountingSink extends ForwardingSink {
    private long bytesWritten = 0;

    public CountingSink(Sink delegate) {
        super(delegate);
    }

    @Override
    public void write(Buffer source, long byteCount) throws IOException {
        bytesWritten += byteCount;
        listener.transferred(key, (int) (100F * bytesWritten / contentLength()));
        super.write(source, byteCount);
        delegate().flush(); // I have added this line to manually flush the sink
    }
}

}

最后,您需要以下代码:

ApiService service = ApiClient.createService(ApiService.class);

        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        builder.addFormDataPart("files",file1.getName(), RequestBody.create(MediaType.parse("video/*"), file1));
        builder.addFormDataPart("files",file3.getName(), RequestBody.create(MediaType.parse("video/*"), file3));

        MultipartBody requestBody = builder.build();

        CountingFileRequestBody requestBody1 = new CountingFileRequestBody(requestBody, "files", new CountingFileRequestBody.ProgressListener() {
            @Override
            public void transferred(String key, int num) {
                Log.d("FinishAdapter","Perecentae is :"+num);
                //update progressbar here
                dialog.updateProgress(num);
                if (num == 100){
                    dialog.dismiss();
                }

            }
        });

        Call<ResponseBody> call = service.postMeme(requestBody1);
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
               // Toast.makeText(getBaseContext(),"All fine",Toast.LENGTH_SHORT).show();
                Log.d("FinishAdapter","every thing is ok............!");
                Log.d("FinishAdapter",response.toString());
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                //Toast.makeText(getBaseContext(),t.getMessage(),Toast.LENGTH_SHORT).show();
                Log.d("FinishAdapter","every thing is failed............!");
            }
        });

希望能帮助到你。


0

用于创建零件的扩展。回调将在调用服务期间被调用

fun File.toPart(type: String = "image/*", callback: (progress: Int)->Unit) = MultipartBody.Part.createFormData(name, name, object : RequestBody() {
    val contentType = MediaType.parse(type)
    val length = this@toPart.length()
    var uploaded = 0L
    override fun contentType(): MediaType? {
        return contentType
    }

    override fun contentLength(): Long = length

    @Throws(IOException::class)
    override fun writeTo(sink: BufferedSink) {
        var source: Source? = null
        try {
            source = Okio.source(this@toPart)

            do {
                val read = source.read(sink.buffer(), 2048)
                if(read == -1L) return // exit at EOF
                sink.flush()
                uploaded += read
                callback((uploaded.toDouble()/length.toDouble()*100).toInt())
            } while(true)
            //sink.writeAll(source!!)
        } finally {
            Util.closeQuietly(source)
        }
    }
})

0

我知道这个问题早在几年前就已经回答了,但是我想我会为Kotlin更新它:

创建一个扩展RequestBody的类。确保填充ContentType枚举类以使用您需要支持的任何内容类型。

class RequestBodyWithProgress(
    private val file: File,
    private val contentType: ContentType,
    private val progressCallback:((progress: Float)->Unit)?
) : RequestBody() {

    override fun contentType(): MediaType? = MediaType.parse(contentType.description)

    override fun contentLength(): Long = file.length()

    override fun writeTo(sink: BufferedSink) {
        val fileLength = contentLength()
        val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
        val inSt = FileInputStream(file)
        var uploaded = 0L
        inSt.use {
            var read: Int = inSt.read(buffer)
            val handler = Handler(Looper.getMainLooper())
            while (read != -1) {
                progressCallback?.let {
                    uploaded += read
                    val progress = (uploaded.toDouble() / fileLength.toDouble()).toFloat()
                    handler.post { it(progress) }

                    sink.write(buffer, 0, read)
                }
                read = inSt.read(buffer)
            }
        }
    }

    enum class ContentType(val description: String) {
        PNG_IMAGE("image/png"),
        JPG_IMAGE("image/jpg"),
        IMAGE("image/*")
    }
}

使用翻新上传文件:

fun uploadFile(fileUri: Uri, progressCallback:((progress: Float)->Unit)?) {
    val file = File(fileUri.path)
    if (!file.exists()) throw FileNotFoundException(fileUri.path)

    // create RequestBody instance from file
    val requestFile = RequestBodyWithProgress(file, RequestBodyWithProgress.ContentType.PNG_IMAGE, progressCallback)

    // MultipartBody.Part is used to send also the actual file name
    val body = MultipartBody.Part.createFormData("image_file", file.name, requestFile)

    publicApiService().uploadFile(body).enqueue(object : Callback<MyResponseObj> {
        override fun onFailure(call: Call<MyResponseObj>, t: Throwable) {

        }

        override fun onResponse(call: Call<MyResponseObj>, response: Response<MyResponseObj>) {

        }
    })

}

0

我请求@Yuriy Kolbasinskiy给定答案,但是在我对WriteTo()函数进行一些更改后,它为我提供了“预期3037038字节但收到3039232”的错误。答案在科特林中,如下所示:

override fun writeTo(sink: BufferedSink) {
    var uploaded:Long = 0
    var source: Source? = null
    try {
        source = Okio.source(file)
        val handler = Handler(Looper.getMainLooper())

        do {
            val read = source.read(sink.buffer(), 2048)
            while (read == -1L) return
            uploaded += read

            handler.post(ProgressUpdater(uploaded, file.length()))
            sink.flush()
        } while(true)
    } finally {
        Util.closeQuietly(source)
    }
}

-2

您可以使用使用Retrofit库的FileUploader连接到API。要上传文件,代码框架如下:

FileUploader fileUploader = new FileUploader();
fileUploader.uploadFiles("/", "file", filesToUpload, new FileUploader.FileUploaderCallback() {
    @Override
    public void onError() {
        // Hide progressbar
    }

    @Override
    public void onFinish(String[] responses) {
        // Hide progressbar

        for(int i=0; i< responses.length; i++){
            String str = responses[i];
            Log.e("RESPONSE "+i, responses[i]);
        }
    }

    @Override
    public void onProgressUpdate(int currentpercent, int totalpercent, int filenumber) {
        // Update Progressbar
        Log.e("Progress Status", currentpercent+" "+totalpercent+" "+filenumber);
    }
});

完整步骤可在“中”找到:

在Android中改进多文件上传功能


1
您复制了我的代码,在没有任何原始帖子链接的情况下撰写了一篇文章。伙计,那太不好了
Yuriy Kolbasinskiy

这不是很好的张,请记入原始海报。
humble_wolf

@YuriyKolbasinskiy我可能使用过您的代码段。请发送您的帖子的链接。我会给你积分。:)
Sheetal Kumar Maurya
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.