【发布时间】:2014-08-08 09:29:39
【问题描述】:
在 Android 中使用 OKhttp 在多部分中上传单个大文件(更具体地说,到 s3)有哪些选择?
【问题讨论】:
-
你能更新使用的代码来解决这个问题吗?
-
这些解决方案都不能处理“大”文件。请将它们全部删除,以便记录真正的解决方案。
标签: android file-upload upload okhttp
在 Android 中使用 OKhttp 在多部分中上传单个大文件(更具体地说,到 s3)有哪些选择?
【问题讨论】:
标签: android file-upload upload okhttp
从OkHttp Recipes page,此代码将图像上传到 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());
获取 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;
}
#Edited for 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();
#Uploaded Demo on GITHUB: ##我已经为Multiple Image Upload 添加了我的答案:)
【讨论】:
JSONObject 返回值。检查一下。
MediaType.parse("application/pdf")
对于 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();
}
}
【讨论】:
在 Android 中,您通常与 Uri 分开。使用大文件时的问题是,如果您尝试将完整流读取到字节数组(内存中的所有内容),或者最终使用 Uri 流创建无用文件,您很容易遇到OutOfMemoryError。这是因为RequestBody 不支持从Stream 创建(因为有时OkHttp 需要多次读取,例如,如果您获得30X 重定向)或Uri(因为OkHttp 不是Android 库)。
但是 OkHttp 提供了 Okio 库,提供了方便的类模拟 Streams(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 中发帖和评论
【讨论】: