web-dev-qa-db-fra.com

Retrofit 2 ne peut pas télécharger un fichier avec deux paramètres de chaîne distincts supplémentaires

Lire le texte au bas de la question pour une solution de remplacement possible jusqu'à ce que la solution soit trouvée.}

C'est un fichier de publication réussi avec deux paramètres utilisant POSTMan. J'essaie de faire la même chose avec la modification mais reçois BadRequest. 

Réglages PostMan:

 enter image description here

Détails de publication sur le réseau Chrome: enter image description here

Maintenant, voici comment je le fais dans Android mais en échouant:

Interface de service de modernisation:

@Multipart
@POST("jobDocuments/upload")
Call<ResponseBody> upload(@Part("file") MultipartBody.Part file,@Part("folder") MultipartBody.Part folder,@Part("name") MultipartBody.Part name);

Ceci est ma méthode @Background pour exécuter la requête réseau avec le service ci-dessus généré.

CustDataClient service =
            ServiceGenerator.createService(CustDataClient.class);
    File file = new File(fileUri.getPath());
    // create RequestBody instance from file
    RequestBody requestFile =
            RequestBody.create(MediaType.parse("multipart/form-data"), file);

    MultipartBody.Part fileData =
            MultipartBody.Part.createFormData("file", fileName, requestFile);
    MultipartBody.Part folder =
            MultipartBody.Part.createFormData("folder", "LeadDocuments");
    MultipartBody.Part name =
            MultipartBody.Part.createFormData("name", fileName);
    // finally, execute the request
    Call<ResponseBody> call = service.upload(fileData,folder,name);
    try {
        Response<ResponseBody> rr = call.execute();
        ResponseBody empJobDocsResult = rr.body();//Bad Request here :(
        Log.v("Upload", "success");
    } catch (Exception ex) {
        Log.e("Upload error:", ex.getMessage());
    }

Voici ma méthode Api Web:

 [Route("upload")]
    [HttpPost]
    public IHttpActionResult Upload()
    {
        if (HttpContext.Current.Request.Files.AllKeys.Any())
        {
            // Get the uploaded image from the Files collection
            var httpPostedFile = HttpContext.Current.Request.Files["file"];

            if (httpPostedFile != null)
            {
                // Validate the uploaded image(optional)
                var folder = HttpContext.Current.Request.Form["folder"];
                var fileName = HttpContext.Current.Request.Form["name"];
                fileName = string.IsNullOrEmpty(fileName) ? httpPostedFile.FileName : fileName;
                // Get the complete file path
                var fileSavePath = Path.Combine(HttpContext.Current.Server.MapPath("~/Files/" + folder), fileName);

                // Save the uploaded file to "UploadedFiles" folder
                httpPostedFile.SaveAs(fileSavePath);

                return Ok(new OkMessage { Message = "File uploaded successfully", Path = "/Files/" + folder + "/" + fileName });
            }
        }

        return BadRequest("File not uploaded");
    }

S'il vous plaît, aidez là où je me trompe et comment y parvenir. Existe-t-il une alternative simple au retrofit?

[Edit] Ce code fonctionne correctement, grâce à koush/ion :

Ion.with(getContext())
                            .load("POST", "http://www.dgheating.com/api/jobDocuments/upload")
                            .setMultipartParameter("folder", "LeadDocuments")
                            .setMultipartParameter("name", fileName)
                            .setMultipartFile("file", new File(imagePath))
                            .asJsonObject()
                            .setCallback(...);
13
ahmadalibaloch

J'ai rencontré un problème similaire ici: Android avec Retrofit2 OkHttp3 - Multipart POST Erreur

Mon problème a été résolu après avoir pris la suggestion de @TommySM. Si cela ne vous est pas encore clair, je pense que c'est la solution:

@Multipart
@POST("jobDocuments/upload")
Call<ResponseBody> upload(
    @Part MultipartBody.Part file,
    @Part("folder") RequestBody folder,
    @Part("name")   RequestBody name);

File file = new File(fileUri.getPath());

// Assume your file is PNG
RequestBody requestFile =
        RequestBody.create(MediaType.parse("image/png"), file);

MultipartBody.Part fileData =
        MultipartBody.Part.createFormData("file", fileName, requestFile);

RequestBody folder = RequestBody.create(
        MediaType.parse("text/plain"),
        "LeadDocuments");

RequestBody name = RequestBody.create(
        MediaType.parse("text/plain"),
        fileName);

// finally, execute the request
Call<ResponseBody> call = service.upload(fileData, folder, name);

La partie importante est d’utiliser MediaType.parse("text/plain") pour le paramètre MediaType of String (je pense que votre cas est: paramètre folder & name), utiliser okhttp3.MultipartBody.FORM est une erreur.

Voir ces captures d'écran pour la comparaison:

1) POST problématique

 enter image description here

2) correct post

 enter image description here

7
Shuwn Yuan Tee

Donc, espérons qu'il n'est pas trop tard, et si c'est le cas - cela pourrait aider quelqu'un d'autre :) mes 2 centimes à ce sujet sont après avoir eu le même problème il y a quelque temps:

La définition de service, utilisant uniquement @Part (modifiez les noms de champs en fonction des attentes de votre serveur)

//Single image MultiPart
@Multipart
@POST("user/imageupload")
Call<ResponseBody> upload(@Part("userfile") RequestBody file, @Part("userid") RequestBody description);

Et pour le tour de magie, je me réfère aux deux parties juste comme RequestBody, en utilisant la fonction MediaType.parse() à chacune avec son propre type, en s'appuyant sur la définition de @Multipart pour la demande elle-même, aucun besoin de form-data, les mêmes travaux pour plusieurs fichiers, et plusieurs champs:

private static final String IMG_JPEG = "image/jpeg";
private static final String TXT_PLAIN = "text/plain";

public void uploadImageMultipart(Uri uri, final CustomEventListener<String> listener)
{
    RequestBody fileBody;
    RequestBody textBody;
    File file = new File(uri.getPath());
    Call<ResponseBody> requestCall;

    fileBody = RequestBody.create(okhttp3.MediaType.parse(IMG_JPEG), file);
    textBody = RequestBody.create(okhttp3.MediaType.parse(TXT_PLAIN), String.valueOf(SettingsManager.getUserID()));

      requestCall = serviceCaller.upload(fileBody, textBody);
      requestCall.enqueue(new Callback<ResponseBody>()
      {
        @Override
        public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> rawResponse)
        {
            try
            {
                String response = rawResponse.body().string();
                //from here it's your show....
                listener.getResult("Got it");
            }
            catch (Exception e)
            {
                        e.printStackTrace();
            }
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable throwable)
        {

        }
    });
}

(J'utilise un écouteur pour renvoyer la réponse au rappel à une autre partie de l'application (par exemple, une activité d'appel)).

Ceci envoie définitivement le fichier/s et le champ de texte, d’autres problèmes proviendraient probablement du côté serveur.

J'espère que cela t'aides!

6
TommySM

Interface pour la modernisation (API)

public interface API {

    String NAME = "name";
    String FOLDER = "folder";
    @Multipart
    @POST("/api/jobDocuments/upload")
    Call<JsonResponse> uploadFile(
        @Part(NAME) String name,
        @Part(FOLDER) String folder,
        @Part MultipartBody.Part file);
}

Classe de réponse (JsonResponse)

public class JsonResponse {

    @SerializedName("$id")
    public String id;
    @SerializedName("Message")
    public String message;
    @SerializedName("Path")
    public String path;

    public JsonResponse(String id, String message, String path) {
        this.id = id;
        this.message = message;
        this.path = path;
    }
}

Appel API depuis l'application

Retrofit retrofit;

private void postImage() {
    String URL = "http://www.dgheating.com/";

    //your file location
    File file = new File(Environment.getExternalStorageDirectory() + "/Image.png");

    //parameters
    String NAME = file.getName();
    String FOLDER = "LeadDocuments";
    String FILE = "file";

    retrofit = new Retrofit.Builder()
            .baseUrl(URL)
            .addConverterFactory(GsonConverterFactory.create(new Gson()))
            .build();
    API api = retrofit.create(API.class);
    RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
    MultipartBody.Part part = MultipartBody.Part.createFormData(FILE, NAME, requestBody);
    Call<JsonResponse> call = api.uploadFile(NAME, FOLDER, part);

    call.enqueue(new Callback<JsonResponse>() {
        @Override
        public void onResponse(Call<JsonResponse> call, Response<JsonResponse> response) {
            //response.body()           null
            //response.code()           500

            //https://github.com/square/retrofit/issues/1321
            Converter<ResponseBody, JsonResponse> errorConverter
                    = retrofit.responseBodyConverter(JsonResponse.class, new Annotation[0]);
            try {
                JsonResponse jsonResponse = errorConverter.convert(response.errorBody());
                Log.e("error", "id:" + jsonResponse.id);                //1
                Log.e("error", "message:" + jsonResponse.message);      //An error has occurred
                Log.e("error", "path:" + jsonResponse.path);             //null
            } catch (IOException ignored) {
            }
        }

        @Override
        public void onFailure(Call<JsonResponse> call, Throwable t) {

        }
    });
}

Erreur dans les champs, en raison de l'un de ces problèmes de serveur

  1. Google
  2. Réponse HTTP 500 utilisant retrofit, mais la requête de Post par Postman donne la réponse souhaitée
  3. Comment obtenir du corps de réponse pour mettre à niveau l'exception?
  4. Retrofit SocketTimeoutException (et/ou erreur http 500) sur http-POST
1
shanraisshan

Il semble que votre définition de service soit erronée. Essayer

@Multipart
@POST("jobDocuments/upload")
Call<ResponseBody> upload(@Part("file") MultipartBody.Part file,@Part("folder") RequestBody folder,@Part("name") RequestBody name);

et

RequestBody folder =
        RequestBody.create(
                MediaType.parse("multipart/form-data"), "LeadDocuments");
RequestBody name =
        RequestBody.create(
                MediaType.parse("multipart/form-data"), filename);   

dans votre méthode @Background. Tout cela suppose que vous utilisez Retrofit2.

0
JohnWowUs