android - peticiones - retrofit implementation java
POST Datos de formulario multiparte utilizando Retrofit 2.0, incluida la imagen (7)
Estoy tratando de hacer una POST HTTP al servidor usando 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();
El servidor devuelve un error diciendo que el archivo no es válido.
Esto es extraño porque he intentado cargar el mismo archivo con el mismo formato en iOS (usando otra biblioteca), pero se carga con éxito.
Me pregunto cuál es la forma correcta de cargar una imagen usando Retrofit 2.0 .
¿Debo guardarlo en el disco antes de cargarlo?
PD: He utilizado la actualización para otra solicitud de varias partes que no incluye imagen y se completaron con éxito. El problema es cuando intento incluir un byte en el cuerpo.
Código de actualización para la carga de archivos de imagen en 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);
}
Cambie
MediaType.parse("image/*")
a
MediaType.parse("image/jpeg")
RequestBody reqFile = RequestBody.create(MediaType.parse("image/jpeg"), file);
RequestBody email = RequestBody.create(MediaType.parse("text/plain"), "[email protected]");
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();
}
});
Cargar archivos usando Retrofit es bastante simple. Necesita construir su interfaz api como
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);
}
en la imagen de código anterior está el nombre de la clave, por lo que si está utilizando php, escribirá $ _FILES [''image''] [''tmp_name''] para obtener esto. Y filename = "myfile.jpg" es el nombre del archivo que se envía con la solicitud.
Ahora para cargar el archivo necesita un método que le dará la ruta absoluta desde el 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;
}
Ahora puede usar el siguiente código para cargar su archivo.
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();
}
});
}
Para obtener una explicación más detallada, puede visitar este Tutorial de actualización de archivos de carga .
Entonces, es una forma muy simple de lograr su tarea. Debe seguir el siguiente paso: -
1. Primer paso
public interface APIService {
@Multipart
@POST("upload")
Call<ResponseBody> upload(
@Part("item") RequestBody description,
@Part("imageNumber") RequestBody description,
@Part MultipartBody.Part imageFile
);
}
@Multipart request
realizar la llamada completa como
@Multipart request
.
item
e
image number
es solo el cuerpo de la cadena que está envuelto en
RequestBody
.
Utilizamos la
MultipartBody.Part class
que nos permite enviar el nombre del archivo real además de los datos del archivo binario con la solicitud
2. Segundo paso
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);
Ahora tiene la
image path
y necesita convertirla en
file
. Ahora convierta el
file
en
RequestBody
usando el método
RequestBody.create(MediaType.parse("multipart/form-data"), file)
.
Ahora necesita convertir su
RequestBody requestFile
en
MultipartBody.Part
usando el método
MultipartBody.Part.createFormData("Image", file.getName(), requestBody);
.
ImageNumber
y
ItemId
son mis otros datos que necesito enviar al servidor, por lo que también hago ambas cosas en
RequestBody
.
Estoy destacando la solución en 1.9 y 2.0 ya que es útil para algunos
En
1.9
, creo que la mejor solución es guardar el archivo en el disco y usarlo como archivo Mecanografiado como:
RetroFit 1.9
(No sé acerca de su implementación del lado del servidor) tiene un método de interfaz API similar a este
@POST("/en/Api/Results/UploadFile")
void UploadFile(@Part("file")TypedFile file,@Part("folder")String folder,Callback<Response> callback);
Y úsalo como
TypedFile file = new TypedFile("multipart/form-data", new File(path));
Para RetroFit 2, use el siguiente método
RetroFit 2.0 (Esta fue una solución para un issue en RetroFit 2 que se solucionó ahora; para el método correcto, consulte la respuesta de jimmy0251 )
Interfaz 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);
}
Úselo como:
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();
}
});
Hay una forma correcta de cargar un archivo con su nombre con Retrofit 2 , sin ningún truco :
Definir interfaz API:
@Multipart
@POST("uploadAttachment")
Call<MyResponse> uploadAttachment(@Part MultipartBody.Part filePart);
// You can add other parameters too
Sube un archivo como este:
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);
Esto demuestra solo la carga de archivos, también puede agregar otros parámetros en el mismo método con la anotación
@Part
.
Utilicé Retrofit 2.0 para mis usuarios registrados, envié una imagen de archivo multipart / form y texto desde la cuenta de registro
En mi RegisterActivity, use una 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) {..}
Y en mi clase Register.java es donde usar Retrofit con llamada sincrónica
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();
}
}
}
En la interfaz de 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
);
}
Para el análisis de utilidades de la respuesta InputStream
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;
}
}
Agregando a la respuesta dada por @insomniac
.
Puede crear un
Map
para colocar el parámetro para
RequestBody
incluida la imagen.
Código para la interfaz
public interface ApiInterface {
@Multipart
@POST("/api/Accounts/editaccount")
Call<User> editUser (@Header("Authorization") String authorization, @PartMap Map<String, RequestBody> map);
}
Código para la clase 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();
}
});