我目前正在使用Retrofit 2
,我想在我的服务器上传一些照片。
我知道,旧版本使用TypedFile
类进行上传。如果我们想要使用进度条,我们应该覆盖writeTo
类中的TypedFile
方法。
使用retrofit 2
库时是否可以显示进度?
答案 0 :(得分:146)
首先,您应该使用等于或高于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 out 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 incase 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 queue
progressBar.setProgress(100);
}
}
答案 1 :(得分:17)
修改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();
})
}
答案 2 :(得分:9)
以下是使用简单的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
这是RequestBody.writeTo()被多次调用的结果,因为默认的contentLength()是-1。
无论如何,这花了很长时间才弄明白,希望它有所帮助。
答案 3 :(得分:2)
@ 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();
}
}
}
答案 4 :(得分:1)
我更新了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();
}
}
答案 5 :(得分:1)
答案 6 :(得分:1)
避免两次运行问题。我们最初可以将标志设置为零,并在第一次调用进度对话框后将标志设置为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);
}
}
答案 7 :(得分:1)
这是 Kotlin 的扩展函数。
/** Returns a new request body that transmits the content of this. */
fun File.asRequestBodyWithProgress(
contentType: MediaType? = null,
progressCallback: ((progress: Float) -> Unit)?
): RequestBody {
return object : RequestBody() {
override fun contentType() = contentType
override fun contentLength() = length()
override fun writeTo(sink: BufferedSink) {
val fileLength = contentLength()
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
val inSt = FileInputStream(this@asRequestBodyWithProgress)
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)
}
}
}
}
}
这里是上面函数的用法和 Flow
private val key = "file"
private val multiPart = "multipart/form-data".toMediaType()
@WorkerThread
fun uploadFile(
path: String,
onStart: () -> Unit,
onComplete: () -> Unit,
onProgress: (progress: Float) -> Unit,
onError: (String?) -> Unit
) = flow {
val file = File(path)
val requestFile = file.asRequestBodyWithProgress(multiPart, onProgress)
val requestBody = MultipartBody.Part.createFormData(key, file.name, requestFile)
//val requestBody = MultipartBody.Builder().addFormDataPart(key, file.name, requestFile).build()
val response = uploadClient.uploadFile(requestBody)
response.suspendOnSuccess {
data.whatIfNotNull {
emit(it)
}
}.onError {
/** maps the [ApiResponse.Failure.Error] to the [ErrorResponse] using the mapper. */
map(ErrorResponseMapper) { onError("[Code: $code]: $message") }
}.onException { onError(message) }
}.onStart { onStart() }.onCompletion { onComplete() }.flowOn(Dispatchers.IO)
更新 -> 从 API 30 开始,很难获得文件的真实路径。所以我们可以使用下面提到的 InputStream。
@WorkerThread
fun uploadFile(
path: String,
onStart: () -> Unit,
onComplete: (String?) -> Unit,
onProgress: (progress: Float) -> Unit,
onError: (String?) -> Unit
) = flow {
openStream(path).whatIfNotNull { inputStream ->
val requestFile = inputStream.asRequestBodyWithProgress(MultipartBody.FORM, onProgress)
val requestBody = MultipartBody.Part.createFormData(key, "file", requestFile)
//val requestBody = MultipartBody.Builder().addFormDataPart(key, file.name, requestFile).build()
uploadClient.uploadFile(requestBody).suspendOnSuccess {
data.whatIfNotNull {
link = it.link
emit(it)
}
}.onError {
/** maps the [ApiResponse.Failure.Error] to the [ErrorResponse] using the mapper. */
map(ErrorResponseMapper) { onError("[Code: $code]: $message") }
}.onException { onError(message) }
}
}.onStart { onStart() }.onCompletion { onComplete(link) }.flowOn(Dispatchers.IO)
private fun openStream(path: String): InputStream? {
return context.contentResolver.openInputStream(Uri.parse(path))
}
/** Returns a new request body that transmits the content of this. */
fun InputStream.asRequestBodyWithProgress(
contentType: MediaType? = null,
progressCallback: ((progress: Float) -> Unit)?
): RequestBody {
return object : RequestBody() {
override fun contentType() = contentType
override fun contentLength() = try {
available().toLong()
} catch (e: IOException){
Timber.e(e)
0
}
override fun writeTo(sink: BufferedSink) {
val fileLength = contentLength()
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
val inputStream = this@asRequestBodyWithProgress
var uploaded = 0L
inputStream.use {
var read: Int = inputStream.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 = inputStream.read(buffer)
}
}
}
}
}
答案 8 :(得分:0)
据我在this帖子中所看到的,没有关于图片上传进度响应的更新,您仍然需要override
this所示的writeTo
方法通过制作ProgressListener
界面并使用TypedFile
到override
writeTo
方法的子类来回答
因此,在使用改装2库时,不是任何显示进度的内置方式。
答案 9 :(得分:0)
从httpbuilder
中删除Http Logging拦截器。否则它会拨打writeTo()
两次。或者从BODY
更改日志记录级别。
答案 10 :(得分:0)
您可以使用使用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);
}
});
可以在“中”找到完整步骤:
答案 11 :(得分: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............!");
}
});
希望有帮助。
答案 12 :(得分: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)
}
}
})
答案 13 :(得分: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>) {
}
})
}
答案 14 :(得分:0)
我请求@Yuriy Kolbasinskiy给出答案,但它给我带来了错误 我更改WriteTo()函数的某些内容后,“预期3037038字节但收到3039232”。答案是在Kotlin中给出的:-
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)
}
}
答案 15 :(得分:0)
这是一个受此线程启发很大的扩展函数,它运行得非常好。
fun File.toRequestBody(progressCallback: ((progress: Int) -> Unit)?): RequestBody {
return object : RequestBody() {
private var currentProgress = 0
private var uploaded = 0L
override fun contentType(): MediaType? {
val fileType = name.substringAfterLast('.', "")
return fileType.toMediaTypeOrNull()
}
@Throws(IOException::class)
override fun writeTo(sink: BufferedSink) {
source().use { source ->
do {
val read = source.read(sink.buffer, 2048)
if (read == -1L) return // exit at EOF
sink.flush()
uploaded += read
/**
* The value of newProgress is going to be in between 0.0 - 2.0
*/
var newProgress = ((uploaded.toDouble() / length().toDouble()))
/**
* To map it between 0.0 - 100.0
* Need to multiply it with 50
* (OutputMaxRange/InputMaxRange)
* 100 / 2 = 50
*/
newProgress = (50 * newProgress)
if (newProgress.toInt() != currentProgress) {
progressCallback?.invoke(newProgress.toInt())
}
currentProgress = newProgress.toInt()
} while (true)
}
}
}
}