我该如何在Android中使用OKhttp对单个大文件(更具体地说,是上传到s3)进行分块上传?
我该如何在Android中使用OKhttp对单个大文件(更具体地说,是上传到s3)进行分块上传?
获取OkHttp 2.1,并使用MultipartBuilder.addFormDataPart()
,该方法接受文件名作为参数。
/**
* Upload Image
*
* @param memberId
* @param sourceImageFile
* @return
*/
public static JSONObject uploadImage(String memberId, String sourceImageFile) {
try {
File sourceFile = new File(sourceImageFile);
Log.d(TAG, "File...::::" + sourceFile + " : " + sourceFile.exists());
//Determining the media type
final MediaType MEDIA_TYPE = sourceImageFile.endsWith("png") ?
MediaType.parse("image/png") : MediaType.parse("image/jpeg");
RequestBody requestBody = new MultipartBuilder()
.type(MultipartBuilder.FORM)
.addFormDataPart("member_id", memberId)
.addFormDataPart("file", "profile.png", RequestBody.create(MEDIA_TYPE, sourceFile))
.build();
Request request = new Request.Builder()
.url(URL_UPLOAD_IMAGE)
.post(requestBody)
.build();
OkHttpClient client = new OkHttpClient();
Response response = client.newCall(request).execute();
return new JSONObject(response.body().string());
} catch (UnknownHostException | UnsupportedEncodingException e) {
Log.e(TAG, "Error: " + e.getLocalizedMessage());
} catch (Exception e) {
Log.e(TAG, "Other Error: " + e.getLocalizedMessage());
}
return null;
}
#为okhttp3进行了编辑:
compile 'com.squareup.okhttp3:okhttp:3.4.1'
RequestBody被替换为:
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("uploaded_file", filename, RequestBody.create(MEDIA_TYPE_PNG, sourceFile))
.addFormDataPart("result", "my_image")
.build();
JSONObject
类型的返回值。请检查它。 - Pratik ButaniMediaType.parse("application/pdf")
- Pratik Butani从 OkHttp Recipes 页面,这段代码将图像上传到 Imgur:
private static final String IMGUR_CLIENT_ID = "...";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
RequestBody requestBody = new MultipartBuilder()
.type(MultipartBuilder.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"title\""),
RequestBody.create(null, "Square Logo"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"image\""),
RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
.url("https://api.imgur.com/3/image")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
你需要将其适应于S3,但你所需的类应该是相同的。
multipartBuilder.addFormDataPart("image", fileName, entry.getValue());
- shoke对于 okhttp 4.* 版本,请使用 MultipartBody.Builder
:
fun postMultipart(url: String, text: String, imagePath: String, imageFileName: String): okhttp3.Response? {
val file = File(imagePath)
val fileRequestBody = file.asRequestBody("image/jpeg".toMediaType())
val requestBody = MultipartBody.Builder()
.addFormDataPart("text", text)
.addFormDataPart("image", imageFileName, fileRequestBody)
.build()
val request = getRequestBuilder(url)
.post(requestBody)
.build()
val client = OkHttpClient()
client.newCall(request).execute().use { response ->
return response
}
}
对于OkHttp 2.6.0版本,
try {
File file = new File(Environment.getExternalStorageDirectory().getPath()+"/xxx/share/" + "ic_launcher.png");
String contentType = file.toURL().openConnection().getContentType();
RequestBody fileBody = RequestBody.create(MediaType.parse(contentType), file);
RequestBody requestBody = new MultipartBuilder()
.type(MultipartBuilder.FORM)
.addFormDataPart("fileUploadType","1")
.addFormDataPart("miniType",contentType)
.addFormDataPart("ext",file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf(".")))
.addFormDataPart("fileTypeName","img")
.addFormDataPart("Filedata","ss.png",fileBody)
.build();
Request request = new Request.Builder()
.url(Contains.MULTIPARTY_POST)
.post(requestBody)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
tvGetNews.setText("upload fail");
}
});
}
@Override
public void onResponse(Response response) throws IOException {
runOnUiThread(new Runnable() {
@Override
public void run() {
tvGetNews.setText("upload success");
}
});
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
RequestBody
,请使用 Okio 库。我在这里提供了一个示例 https://dev59.com/KGAf5IYBdhLWcg3wyVJ1#68481022 - gmanjonUri
。当使用大文件时,如果尝试将整个流读取到字节数组(即内存中的所有内容),容易遇到OutOfMemoryError
,或者会创建无用的带有Uri流的文件。这是因为RequestBody
不支持从Stream
(因为有时需要多次读取它,例如如果您获得30X重定向)或Uri
(因为OkHttp不是一个Android库)来创建。Source
和Sink
)以及更方便的内部使用。BodyRequest
,从Uri
中避免任何由于大文件而导致的OutOfMemoryError
,请按照以下方式创建:private static final MediaType MULTIPART_FOR_DATA = MediaType.parse("multipart/form-data");
private @NotNull RequestBody getFilePart(Uri largeFileUri) {
return new RequestBody() {
@Override
public MediaType contentType() {
return MULTIPART_FOR_DATA;
}
@Override
public void writeTo(@NotNull BufferedSink sink) throws IOException {
try (Source source = Okio.source(context.getContentResolver().openInputStream(mediaUri))) {
sink.writeAll(source);
}
}
};
}
感谢所有在以下GitHub线程https://github.com/square/okhttp/issues/3585中发帖和评论的人。