使用 Retrofit 2 上传文件时出错
Error uploading file with Retrofit 2
我正在使用的系统有一个接收文件图片的端点,以便设置用户的个人资料图片。如果我尝试通过 Postman 发送它,一切正常:
当我尝试使用改造从我的应用程序上传它时,它总是失败。代码 + 堆栈跟踪如下:
服务器端代码:
begin
user = User.find(doorkeeper_token.resource_owner_id)
image = params[:image]
attachment = {
:filename => image[:filename],
:type => image[:type],
:headers => image[:head],
:tempfile => image[:tempfile]
}
image_link = ""
if params[:upload_type] == "profile"
user.profile_image = ActionDispatch::Http::UploadedFile.new(attachment)
image_link = get_image_path(user.id,params[:upload_type],"original",attachment[:filename])
user.image_profile_original = get_image_path(user.id,params[:upload_type],"original",attachment[:filename])
user.image_profile_large = get_image_path(user.id,params[:upload_type],"large",attachment[:filename])
/*e22*/
@Multipart
@POST("user/upload_image")
fun uploadUserImage(@Header("Authorization") authHeader: String, @Part("upload_type") description: RequestBody, @Part file: MultipartBody.Part): Call<ResponseWrapper>
fun uploadUserImage(image: Uri) {
val source = File(image.path)
val requestFile = RequestBody.create(MediaType.parse(getContext().contentResolver.getType(image)), source)
val body = MultipartBody.Part.createFormData("image", "profile", requestFile)
val description = RequestBody.create(okhttp3.MultipartBody.FORM, "profile")
mMessenger.uploadUserImage(getAuthorizationHeader(), description, body).enqueue(ResponseWrapperCallback(EventCatalog.e0022))
}
请求响应:
E/ResponseWrapperCallback: Failed processing request: profile: open failed: ENOENT (No such file or directory)
E/ResponseWrapperCallback: HTTP request mapped to event e0022 finished with error
编辑 1
我确实认为这个错误与 File 对象的实例化方式有关。如果我直接从 Uri 创建文件,它的真实路径似乎不会发送到服务器。我说是因为上面的错误日志,它说它没有在检索到的设备的 Uri 上找到文件。我试图从 Uri 中提取文件的真实路径,但是,来自服务器的错误日志已更改为:
/storage/emulated/0/Marvel/Media/Characters/Adam Warlock.jpeg: open failed: EACCES (Permission denied)
我用真实文件路径实例化 File 对象的方式是这样的:
private fun getRealPathFromURI(contentURI: Uri): String {
val result: String
val cursor = getContext().contentResolver.query(contentURI, null, null, null, null)
if (cursor == null) {
result = contentURI.path
} else {
cursor.moveToFirst()
val idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)
result = cursor.getString(idx)
cursor.close()
}
return result
}
//....
val source = File(getRealPathFromURI(Uri))
编辑 2
应用户 Anurag Singh 的要求,以下是清单和 OS 信息:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
android {
compileSdkVersion 25
buildToolsVersion "25.0.1"
defaultConfig {
applicationId "***"
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
}
}
编辑 3
这是 Postman 生成的代码,运行良好!
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
RequestBody body = RequestBody.create(mediaType, "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"image\"; filename=\"sample 2.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"upload_type\"\r\n\r\nprofile\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--");
Request request = new Request.Builder()
.url("http://********/user/upload_image")
.post(body)
.addHeader("content-type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW")
.addHeader("authorization", "Bearer ******")
.addHeader("cache-control", "no-cache")
.addHeader("postman-token", "******")
.build();
Response response = client.newCall(request).execute();
尝试在 AndroidManifest.xml
中添加以下权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>.
Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app.
Requesting Permissions at Run Time
我正在使用的系统有一个接收文件图片的端点,以便设置用户的个人资料图片。如果我尝试通过 Postman 发送它,一切正常:
当我尝试使用改造从我的应用程序上传它时,它总是失败。代码 + 堆栈跟踪如下:
服务器端代码:
begin
user = User.find(doorkeeper_token.resource_owner_id)
image = params[:image]
attachment = {
:filename => image[:filename],
:type => image[:type],
:headers => image[:head],
:tempfile => image[:tempfile]
}
image_link = ""
if params[:upload_type] == "profile"
user.profile_image = ActionDispatch::Http::UploadedFile.new(attachment)
image_link = get_image_path(user.id,params[:upload_type],"original",attachment[:filename])
user.image_profile_original = get_image_path(user.id,params[:upload_type],"original",attachment[:filename])
user.image_profile_large = get_image_path(user.id,params[:upload_type],"large",attachment[:filename])
/*e22*/
@Multipart
@POST("user/upload_image")
fun uploadUserImage(@Header("Authorization") authHeader: String, @Part("upload_type") description: RequestBody, @Part file: MultipartBody.Part): Call<ResponseWrapper>
fun uploadUserImage(image: Uri) {
val source = File(image.path)
val requestFile = RequestBody.create(MediaType.parse(getContext().contentResolver.getType(image)), source)
val body = MultipartBody.Part.createFormData("image", "profile", requestFile)
val description = RequestBody.create(okhttp3.MultipartBody.FORM, "profile")
mMessenger.uploadUserImage(getAuthorizationHeader(), description, body).enqueue(ResponseWrapperCallback(EventCatalog.e0022))
}
请求响应:
E/ResponseWrapperCallback: Failed processing request: profile: open failed: ENOENT (No such file or directory)
E/ResponseWrapperCallback: HTTP request mapped to event e0022 finished with error
编辑 1
我确实认为这个错误与 File 对象的实例化方式有关。如果我直接从 Uri 创建文件,它的真实路径似乎不会发送到服务器。我说是因为上面的错误日志,它说它没有在检索到的设备的 Uri 上找到文件。我试图从 Uri 中提取文件的真实路径,但是,来自服务器的错误日志已更改为:
/storage/emulated/0/Marvel/Media/Characters/Adam Warlock.jpeg: open failed: EACCES (Permission denied)
我用真实文件路径实例化 File 对象的方式是这样的:
private fun getRealPathFromURI(contentURI: Uri): String {
val result: String
val cursor = getContext().contentResolver.query(contentURI, null, null, null, null)
if (cursor == null) {
result = contentURI.path
} else {
cursor.moveToFirst()
val idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)
result = cursor.getString(idx)
cursor.close()
}
return result
}
//....
val source = File(getRealPathFromURI(Uri))
编辑 2
应用户 Anurag Singh 的要求,以下是清单和 OS 信息:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
android {
compileSdkVersion 25
buildToolsVersion "25.0.1"
defaultConfig {
applicationId "***"
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
}
}
编辑 3
这是 Postman 生成的代码,运行良好!
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
RequestBody body = RequestBody.create(mediaType, "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"image\"; filename=\"sample 2.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"upload_type\"\r\n\r\nprofile\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--");
Request request = new Request.Builder()
.url("http://********/user/upload_image")
.post(body)
.addHeader("content-type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW")
.addHeader("authorization", "Bearer ******")
.addHeader("cache-control", "no-cache")
.addHeader("postman-token", "******")
.build();
Response response = client.newCall(request).execute();
尝试在 AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>.
Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. Requesting Permissions at Run Time