POST多部分表单数据使用Retrofit 2.0包括图像

我正在尝试使用Retrofit 2.0进行HTTP POST到服务器

MediaType MEDIA_TYPE_TEXT = MediaType.parse("text/plain"); MediaType MEDIA_TYPE_IMAGE = MediaType.parse("image/*"); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); imageBitmap.compress(Bitmap.CompressFormat.JPEG, 90, byteArrayOutputStream); profilePictureByte = byteArrayOutputStream.toByteArray(); Call<APIResults> call = ServiceAPI.updateProfile( RequestBody.create(MEDIA_TYPE_TEXT, emailString), RequestBody.create(MEDIA_TYPE_IMAGE, profilePictureByte)); call.enqueue(); 

服务器返回一个错误说该文件无效。

这很奇怪,因为我试图在iOS上使用相同的格式上传相同的文件(使用其他库),但上传成功。

我想知道什么是使用Retrofit 2.0上传图像的正确方法?

在上传之前,我应该先将它保存到磁盘吗?

谢谢!

PS:我已经使用了其他不包含图像的Multipart请求,并成功完成。 问题是当我试图包含一个字节的身体。

我在1.9和2.0中强调了这个解决scheme,因为它对于一些有用

1.9 ,我认为更好的解决scheme是将文件保存到磁盘并将其用作Typed文件,如:

RetroFit 1.9

(我不知道你的服务器端实现)有一个类似这样的API接口方法

 @POST("/en/Api/Results/UploadFile") void UploadFile(@Part("file")TypedFile file,@Part("folder")String folder,Callback<Response> callback); 

并使用它

 TypedFile file = new TypedFile("multipart/form-data", new File(path)); 

对于RetroFit 2使用以下方法

RetroFit 2.0

API接口:

 public interface ApiInterface { @Multipart @POST("/api/Accounts/editaccount") Call<User> editUser (@Header("Authorization") String authorization, @Part("file\"; filename=\"pp.png\" ") RequestBody file , @Part("FirstName") RequestBody fname, @Part("Id") RequestBody id); } 

像这样使用它:

 File file = new File(imageUri.getPath()); RequestBody fbody = RequestBody.create(MediaType.parse("image/*"), file); RequestBody name = RequestBody.create(MediaType.parse("text/plain"), firstNameField.getText().toString()); RequestBody id = RequestBody.create(MediaType.parse("text/plain"), AZUtils.getUserId(this)); Call<User> call = client.editUser(AZUtils.getToken(this), fbody, name, id); call.enqueue(new Callback<User>() { @Override public void onResponse(retrofit.Response<User> response, Retrofit retrofit) { AZUtils.printObject(response.body()); } @Override public void onFailure(Throwable t) { t.printStackTrace(); } }); 

有一个正确的方式来上传一个名为Retrofit 2的文件,没有任何破解

定义API接口:

 @Multipart @POST("uploadAttachment") Call<MyResponse> uploadAttachment(@Part MultipartBody.Part filePart); // You can add other parameters too 

上传文件,如下所示:

 File file = // initialize file here MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), RequestBody.create(MediaType.parse("image/*"), file)); Call<MyResponse> call = api.uploadAttachment(filePart); 

这只演示file upload,也可以用@Part注解在同一个方法中添加其他参数。

我为我的注册用户使用了Retrofit 2.0,从注册账户发送multipart / form文件图像和文本

在我的RegisterActivity中,使用一个AsyncTask

 //AsyncTask private class Register extends AsyncTask<String, Void, String> { @Override protected void onPreExecute() {..} @Override protected String doInBackground(String... params) { new com.tequilasoft.mesasderegalos.dbo.Register().register(txtNombres, selectedImagePath, txtEmail, txtPassword); responseMensaje = StaticValues.mensaje ; mensajeCodigo = StaticValues.mensajeCodigo; return String.valueOf(StaticValues.code); } @Override protected void onPostExecute(String codeResult) {..} 

而在我的Register.java类中是使用Retrofit和同步调用的地方

 import android.util.Log; import com.tequilasoft.mesasderegalos.interfaces.RegisterService; import com.tequilasoft.mesasderegalos.utils.StaticValues; import com.tequilasoft.mesasderegalos.utils.Utilities; import java.io.File; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Response; /**Created by sam on 2/09/16.*/ public class Register { public void register(String nombres, String selectedImagePath, String email, String password){ try { // create upload service client RegisterService service = ServiceGenerator.createUser(RegisterService.class); // add another part within the multipart request RequestBody requestEmail = RequestBody.create( MediaType.parse("multipart/form-data"), email); // add another part within the multipart request RequestBody requestPassword = RequestBody.create( MediaType.parse("multipart/form-data"), password); // add another part within the multipart request RequestBody requestNombres = RequestBody.create( MediaType.parse("multipart/form-data"), nombres); MultipartBody.Part imagenPerfil = null; if(selectedImagePath!=null){ File file = new File(selectedImagePath); Log.i("Register","Nombre del archivo "+file.getName()); // create RequestBody instance from file RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file); // MultipartBody.Part is used to send also the actual file name imagenPerfil = MultipartBody.Part.createFormData("imagenPerfil", file.getName(), requestFile); } // finally, execute the request Call<ResponseBody> call = service.registerUser(imagenPerfil, requestEmail,requestPassword,requestNombres); Response<ResponseBody> bodyResponse = call.execute(); StaticValues.code = bodyResponse.code(); StaticValues.mensaje = bodyResponse.message(); ResponseBody errorBody = bodyResponse.errorBody(); StaticValues.mensajeCodigo = errorBody==null ?null :Utilities.mensajeCodigoDeLaRespuestaJSON(bodyResponse.errorBody().byteStream()); Log.i("Register","Code "+StaticValues.code); Log.i("Register","mensaje "+StaticValues.mensaje); Log.i("Register","mensajeCodigo "+StaticValues.mensaje); } catch (Exception e){ e.printStackTrace(); } } } 

在RegisterService的接口中

 public interface RegisterService { @Multipart @POST(StaticValues.REGISTER) Call<ResponseBody> registerUser(@Part MultipartBody.Part image, @Part("email") RequestBody email, @Part("password") RequestBody password, @Part("nombre") RequestBody nombre ); } 

对于InputStream响应的Utilitiesparsing

 public class Utilities { public static String mensajeCodigoDeLaRespuestaJSON(InputStream inputStream){ String mensajeCodigo = null; try { BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream, "iso-8859-1"), 8); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); } inputStream.close(); mensajeCodigo = sb.toString(); } catch (Exception e) { Log.e("Buffer Error", "Error converting result " + e.toString()); } return mensajeCodigo; } } 

Retrofit2.0中更新图像file upload的代码

 public interface ApiInterface { @Multipart @POST("user/signup") Call<UserModelResponse> updateProfilePhotoProcess(@Part("email") RequestBody email, @Part("password") RequestBody password, @Part("profile_pic\"; filename=\"pp.png\" ") RequestBody file); } 

MediaType.parse("image/*")更改为MediaType.parse("image/jpeg")

 RequestBody reqFile = RequestBody.create(MediaType.parse("image/jpeg"), file); RequestBody email = RequestBody.create(MediaType.parse("text/plain"), "upload_test4@gmail.com"); RequestBody password = RequestBody.create(MediaType.parse("text/plain"), "123456789"); Call<UserModelResponse> call = apiService.updateProfilePhotoProcess(email,password,reqFile); call.enqueue(new Callback<UserModelResponse>() { @Override public void onResponse(Call<UserModelResponse> call, Response<UserModelResponse> response) { String TAG = response.body().toString(); UserModelResponse userModelResponse = response.body(); UserModel userModel = userModelResponse.getUserModel(); Log.d("MainActivity","user image = "+userModel.getProfilePic()); } @Override public void onFailure(Call<UserModelResponse> call, Throwable t) { Toast.makeText(MainActivity.this,""+TAG,Toast.LENGTH_LONG).show(); } }); 

添加到@insomniac给出的答案 。 您可以创build一个Map来为RequestBody提供参数,包括图像。

接口代码

 public interface ApiInterface { @Multipart @POST("/api/Accounts/editaccount") Call<User> editUser (@Header("Authorization") String authorization, @PartMap Map<String, RequestBody> map); } 

Java类的代码

 File file = new File(imageUri.getPath()); RequestBody fbody = RequestBody.create(MediaType.parse("image/*"), file); RequestBody name = RequestBody.create(MediaType.parse("text/plain"), firstNameField.getText().toString()); RequestBody id = RequestBody.create(MediaType.parse("text/plain"), AZUtils.getUserId(this)); Map<String, RequestBody> map = new HashMap<>(); map.put("file\"; filename=\"pp.png\" ", fbody); map.put("FirstName", name); map.put("Id", id); Call<User> call = client.editUser(AZUtils.getToken(this), map); call.enqueue(new Callback<User>() { @Override public void onResponse(retrofit.Response<User> response, Retrofit retrofit) { AZUtils.printObject(response.body()); } @Override public void onFailure(Throwable t) { t.printStackTrace(); } }); 

使用Retrofit上传文件非常简单您需要将您的api界面构build为

 public interface Api { String BASE_URL = "http://192.168.43.124/ImageUploadApi/"; @Multipart @POST("yourapipath") Call<MyResponse> uploadImage(@Part("image\"; filename=\"myfile.jpg\" ") RequestBody file, @Part("desc") RequestBody desc); } 

在上面的代码图像是关键的名字,所以如果你使用PHP,你会写$ _FILES ['image'] ['tmp_name']来得到这个。 和文件名=“myfile.jpg”是与您的请求一起发送的文件的名称。

现在上传文件,你需要一个方法,将给你从Uri的绝对path。

 private String getRealPathFromURI(Uri contentUri) { String[] proj = {MediaStore.Images.Media.DATA}; CursorLoader loader = new CursorLoader(this, contentUri, proj, null, null, null); Cursor cursor = loader.loadInBackground(); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String result = cursor.getString(column_index); cursor.close(); return result; } 

现在你可以使用下面的代码来上传你的文件。

  private void uploadFile(Uri fileUri, String desc) { //creating a file File file = new File(getRealPathFromURI(fileUri)); //creating request body for file RequestBody requestFile = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file); RequestBody descBody = RequestBody.create(MediaType.parse("text/plain"), desc); //The gson builder Gson gson = new GsonBuilder() .setLenient() .create(); //creating retrofit object Retrofit retrofit = new Retrofit.Builder() .baseUrl(Api.BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); //creating our api Api api = retrofit.create(Api.class); //creating a call and calling the upload image method Call<MyResponse> call = api.uploadImage(requestFile, descBody); //finally performing the call call.enqueue(new Callback<MyResponse>() { @Override public void onResponse(Call<MyResponse> call, Response<MyResponse> response) { if (!response.body().error) { Toast.makeText(getApplicationContext(), "File Uploaded Successfully...", Toast.LENGTH_LONG).show(); } else { Toast.makeText(getApplicationContext(), "Some error occurred...", Toast.LENGTH_LONG).show(); } } @Override public void onFailure(Call<MyResponse> call, Throwable t) { Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show(); } }); } 

有关更详细的解释,您可以访问此Retrofit上传文件教程