改进 – 多部分请求:所需的MultipartFile参数“文件”不存在
我试图使用Retrofit2发送服务器上的文件。 我根据文档做所有事情,但总是得到400服务器错误。
我试图这样做:
RequestBody body = RequestBody.create(MediaType.parse("image/png"), photo); //.......... @Multipart @POST(ADD_PHOTO) Observable<HPSPhotoResponse> addPhoto(@Part("file") RequestBody file);
…就像这样:
MultipartBody.Part part = MultipartBody.Part.createFormData("file", "file", body); //........... @Multipart @POST(ADD_PHOTO) Observable<HPSPhotoResponse> addPhoto(@Part("file") MultipartBody.Part files);
没关系。 结果始终相同“多部分请求:所需的MultipartFile参数'文件'不存在” – 服务器响应。
我会认为服务器上的Spring工作不好,但我在Swift(iOS)上做的等效代码,它的工作原理! 这里服务器看到这个“文件”部分。
Alamofire.upload(method, endpoint, headers: headers, multipartFormData: { multipartFormData in multipartFormData.appendBodyPart(fileURL: self.filePath!, name: "file") }
现在我希望它可以在Android上使用Retrofit。 但是我甚至查看了Retrofit请求的日志,事实上我没有在日志中看到任何“文件”文本。
那有什么问题?
您可以尝试下面的示例代码。 在这个演示应用程序中,我们将从图库中select后上传照片。 希望它有帮助!
build.gradle文件:
dependencies { ... compile 'com.squareup.retrofit2:retrofit:2.0.1' compile 'com.squareup.retrofit2:converter-gson:2.0.1' ... }
WebAPIService.java文件:
public interface WebAPIService { @Multipart @POST("/api/fileupload") Call<ResponseBody> postFile(@Part MultipartBody.Part file, @Part("description") RequestBody description); }
FileActivity.java文件:
... import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; public class FileActivity extends AppCompatActivity { private final Context mContext = this; private final String API_URL_BASE = "http://serverip:port"; private final String LOG_TAG = "BNK"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_file); selectImage(); // selects a photo from Gallery } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == Activity.RESULT_OK && requestCode == 100) { Uri fileUri = data.getData(); if (fileUri != null) { uploadFile(fileUri); // uploads the file to the web service } } } private void uploadFile(Uri fileUri) { String filePath = getRealPathFromUri(fileUri); if (filePath != null && !filePath.isEmpty()) { File file = new File(filePath); if (file.exists()) { Retrofit retrofit = new Retrofit.Builder() .baseUrl(API_URL_BASE) .build(); WebAPIService service = retrofit.create(WebAPIService.class); // creates RequestBody instance from file RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file); // MultipartBody.Part is used to send also the actual filename MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile); // adds another part within the multipart request String descriptionString = "Sample description"; RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString); // executes the request Call<ResponseBody> call = service.postFile(body, description); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { Log.i(LOG_TAG, "success"); } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.e(LOG_TAG, t.getMessage()); } }); } } } private void selectImage() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); startActivityForResult(intent, 100); } public String getRealPathFromUri(final Uri uri) { // DocumentProvider if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(mContext, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } } // DownloadsProvider else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(mContext, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[]{ split[1] }; return getDataColumn(mContext, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { // Return the remote address if (isGooglePhotosUri(uri)) return uri.getLastPathSegment(); return getDataColumn(mContext, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } private String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = { column }; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int index = cursor.getColumnIndexOrThrow(column); return cursor.getString(index); } } finally { if (cursor != null) cursor.close(); } return null; } private boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } private boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } private boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } private boolean isGooglePhotosUri(Uri uri) { return "com.google.android.apps.photos.content".equals(uri.getAuthority()); } }
在我的情况下,服务器没有处理一些翻新发送的头文件。 这就是为什么我必须从改造请求中删除无用的头。 我已经创build了这样的接口函数:
@POST("my/files/photo/") Call<FileUploadResponse> uploadPhoto(@Header("Content-Type") String contentType, @Header("Authorization") String auth, @Body MultipartBody body);
并称之为:
ApiClient.ApiInterface client = ApiClient.getClient(); File file = new File(getPathFromUri(fileUri)); RequestBody fileBody = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file); MultipartBody body = new MultipartBody.Builder().addFormDataPart("file-type", "profile") .addFormDataPart("photo", "image.png", fileBody) .build(); client.uploadPhoto("multipart/form-data; boundary=" + body.boundary(), PrefManager.getInstance().getToken(), body);
看到细节在这里: 上传图片到服务器使用改造2