可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am trying to do a HTTP POST to server using Retrofit 2.0
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();
The server returns an error saying the file is not valid.
This is weird because I have tried to upload the same file with the same format on iOS(using other library), but it uploads successfully.
I am wondering what is the proper way to upload an image using Retrofit 2.0?
Should I save it to disk first before uploading?
Thank you!
P.S.: I have used retrofit for other Multipart request that does not include image and they completed successfully. The problem is when I am trying to include a byte to the body.
回答1:
I am highlighting the solution in both 1.9 and 2.0 since it is useful for some
In 1.9
, I think the better solution is to save the file to disk and use it as Typed file like:
RetroFit 1.9
(I don\'t know about your server-side implementation) have an API interface method similar to this
@POST(\"/en/Api/Results/UploadFile\")
void UploadFile(@Part(\"file\")TypedFile file,@Part(\"folder\")String folder,Callback<Response> callback);
And use it like
TypedFile file = new TypedFile(\"multipart/form-data\", new File(path));
For RetroFit 2 Use the following method
RetroFit 2.0 ( This was a workaround for an issue in RetroFit 2 which is fixed now, for the correct method refer jimmy0251\'s answer)
API Interface:
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);
}
Use it like:
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();
}
});
回答2:
There is a correct way of uploading a file with its name with Retrofit 2, without any hack:
Define API interface:
@Multipart
@POST(\"uploadAttachment\")
Call<MyResponse> uploadAttachment(@Part MultipartBody.Part filePart);
// You can add other parameters too
Upload file like this:
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);
This demonstrates only file uploading, you can also add other parameters in the same method with @Part
annotation.
回答3:
I used Retrofit 2.0 for my register users, send multipart/form File image and text from register account
In my RegisterActivity, use an 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) {..}
And in my Register.java class is where use Retrofit with synchronous call
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();
}
}
}
In the interface of 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
);
}
For the Utilities parse ofr InputStream response
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;
}
}
回答4:
Update Code for image file uploading in Retrofit2.0
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);
}
Change MediaType.parse(\"image/*\")
to 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();
}
});
回答5:
Adding to the answer given by @insomniac. You can create a Map
to put the parameter for RequestBody
including image.
Code for Interface
public interface ApiInterface {
@Multipart
@POST(\"/api/Accounts/editaccount\")
Call<User> editUser (@Header(\"Authorization\") String authorization, @PartMap Map<String, RequestBody> map);
}
Code for Java class
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();
}
});
回答6:
So its very simple way to achieve your task. You need to follow below step :-
1. First step
public interface APIService {
@Multipart
@POST(\"upload\")
Call<ResponseBody> upload(
@Part(\"item\") RequestBody description,
@Part(\"imageNumber\") RequestBody description,
@Part MultipartBody.Part imageFile
);
}
You need to make the entire call as @Multipart request
. item
and image number
is just string body which is wrapped in RequestBody
. We use the MultipartBody.Part class
that allows us to send the actual file name besides the binary file data with the request
2. Second step
File file = (File) params[0];
RequestBody requestFile = RequestBody.create(MediaType.parse(\"multipart/form-data\"), file);
MultipartBody.Part body =MultipartBody.Part.createFormData(\"Image\", file.getName(), requestBody);
RequestBody ItemId = RequestBody.create(okhttp3.MultipartBody.FORM, \"22\");
RequestBody ImageNumber = RequestBody.create(okhttp3.MultipartBody.FORM,\"1\");
final Call<UploadImageResponse> request = apiService.uploadItemImage(body, ItemId,ImageNumber);
Now you have image path
and you need to convert into file
.Now convert file
into RequestBody
using method RequestBody.create(MediaType.parse(\"multipart/form-data\"), file)
. Now you need to convert your RequestBody requestFile
into MultipartBody.Part
using method MultipartBody.Part.createFormData(\"Image\", file.getName(), requestBody);
.
ImageNumber
and ItemId
is my another data which I need to send to server so I am also make both thing into RequestBody
.
For more info
回答7:
Uploading Files using Retrofit is Quite Simple You need to build your api interface as
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);
}
in the above code image is the key name so if you are using php you will write $_FILES[\'image\'][\'tmp_name\'] to get this.
And filename=\"myfile.jpg\" is the name of your file that is being sent with the request.
Now to upload the file you need a method that will give you the absolute path from the Uri.
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;
}
Now you can use the below code to upload your file.
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();
}
});
}
For more detailed explanation you can visit this Retrofit Upload File Tutorial.