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);
関係ありません。結果は常に「Multipart request:Required MultipartFile parameter 'file' is not present」-サーバーの応答です。
サーバーでのSpringの動作は良くないと思いますが、Swift(iOS)で同等のコードを実行すると動作します!ここでサーバーはこの「ファイル」部分を認識します。
Alamofire.upload(method, endpoint, headers: headers,
multipartFormData: { multipartFormData in
multipartFormData.appendBodyPart(fileURL: self.filePath!, name: "file")
}
現在、RetrofitでAndroidで動作します。しかし、Retrofitリクエストのログを調べても、実際にはログに「ファイル」テキストは表示されません。
それのどこが悪いんだい?
次のサンプルコードを試すことができます。このデモアプリでは、ギャラリーから選択した後に写真をアップロードします。それが役に立てば幸い!
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());
}
}
私の場合、サーバーはレトロフィットが送信するヘッダーを処理していませんでした。そのため、改造要求から不要なヘッダーを削除する必要がありました。このようなインターフェイス関数を作成しました:
@Multipart
@POST("my/files/photo/")
Call<FileUploadResponse> uploadPhoto(@Header("Content-Type") String contentType,
@Header("Authorization") String auth,
@Part 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を使用して画像をサーバーにアップロード