web-dev-qa-db-fra.com

POST Données de formulaire multiparties avec Retrofit 2.0, y compris l'image

J'essaye de faire un HTTP POST au serveur en utilisant Retrofit 2.

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();

Le serveur renvoie une erreur en indiquant que le fichier n'est pas valide.

C'est bizarre parce que j'ai essayé de télécharger le même fichier avec le même format sur iOS (en utilisant une autre bibliothèque), mais le téléchargement a réussi.

Je me demande quelle est la bonne façon de télécharger une image en utilisant Retrofit 2.?

Dois-je l'enregistrer sur le disque avant de télécharger?

P.S .: J'ai utilisé la modification pour une autre demande multipart n'incluant pas d'image et elles se sont bien déroulées. Le problème, c'est quand j'essaie d'inclure un octet dans le corps.

124
JayVDiyk

Je souligne la solution dans les versions 1.9 et 2.0 car elle est utile pour certains

Dans 1.9, je pense que la meilleure solution consiste à enregistrer le fichier sur le disque et à l’utiliser comme fichier typé comme:

RetroFit 1.9

(Je ne sais pas à propos de votre implémentation côté serveur) avoir une méthode d'interface API similaire à celle-ci

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

Et l'utiliser comme

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

Pour RetroFit 2 Utilisez la méthode suivante

RetroFit 2.0 (solution de contournement pour un problème dans RetroFit 2, corrigé maintenant; pour la méthode correcte, reportez-vous à réponse de jimmy0251 )

Interface 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);
}

Utilisez-le comme:

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();
    }
});
157
insomniac

Il y a un moyen correct de télécharger un fichier avec son nom avec Retrofit 2, sans aucun bidouille:

Définir l'interface API:

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

Téléchargez le fichier comme ceci:

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);

Ceci illustre uniquement le téléchargement de fichier, vous pouvez également ajouter d'autres paramètres dans la même méthode avec l'annotation @Part.

140
jimmy0251

J'ai utilisé Retrofit 2.0 pour mes utilisateurs de registre, envoi d'une image de fichier multipart/form et du texte du compte de registre

Dans mon RegisterActivity, utilisez une 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) {..}

Et dans ma classe Register.Java, c’est l’utilisation où Retrofit avec appel synchrone

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();
    }
}
}

Dans l'interface 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
);
}

Pour l'analyse des utilitaires de la réponse 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;
}
}
20
Samuel Ivan

Code de mise à jour pour le téléchargement du fichier image dans Retrofit2.

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);
}

Remplacez MediaType.parse("image/*") par 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();

    }
});
13
Java coder

Ajout à la réponse donnée par @insomniac. Vous pouvez créer un Map pour mettre le paramètre pour RequestBody avec une image.

Code pour l'interface

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

Code pour la classe 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();
 }
});
10
Anjana Sharma

C'est donc un moyen très simple d'accomplir votre tâche. Vous devez suivre l'étape ci-dessous: -

1. Première étape

public interface APIService {  
    @Multipart
    @POST("upload")
    Call<ResponseBody> upload(
        @Part("item") RequestBody description,
        @Part("imageNumber") RequestBody description,
        @Part MultipartBody.Part imageFile
    );
}

Vous devez faire tout l'appel en tant que @Multipart request. item et image number est simplement un corps de chaîne entouré de RequestBody. Nous utilisons le MultipartBody.Part class qui nous permet d’envoyer le nom de fichier actuel en plus des données du fichier binaire avec la requête

2. Deuxième étape

  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);

Maintenant, vous avez image path et vous devez convertir en file. Maintenant, convertissez file en RequestBody en utilisant la méthode RequestBody.create(MediaType.parse("multipart/form-data"), file). Vous devez maintenant convertir votre RequestBody requestFile en MultipartBody.Part à l'aide de la méthode MultipartBody.Part.createFormData("Image", file.getName(), requestBody);.

ImageNumber et ItemId sont mes autres données que je dois envoyer au serveur afin que je puisse également créer les deux en RequestBody.

Pour plus d'informations

6
duggu

Le téléchargement de fichiers à l’aide de Retrofit est très simple. Vous devez créer votre interface api

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);

}

dans le code ci-dessus image est le nom de la clé, donc si vous utilisez php, vous écrirez $ _ FILES ['image '] [' tmp_name '] pour l'obtenir. Et filename = "myfile.jpg" est le nom de votre fichier qui est envoyé avec la demande.

Maintenant, pour télécharger le fichier, vous avez besoin d’une méthode qui vous donnera le chemin absolu depuis l’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;
}

Maintenant, vous pouvez utiliser le code ci-dessous pour télécharger votre fichier.

 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();
            }
        });
    }

Pour une explication plus détaillée, vous pouvez visiter ce tutoriel de téléchargement de fichier de modernisation.

2
Belal Khan