wake-up-neo.com

Retrofit 2 kann keine Datei mit zwei zusätzlichen separaten String-Parametern hochladen

Lesen Sie die Bearbeitung am Ende der Frage nach einer möglichen alternativen Lösung, bis die Lösung gefunden wird.

Dies ist eine erfolgreiche Post-Datei mit zwei Parametern, die POSTMan verwenden. Ich versuche dasselbe mit dem Retrofit zu tun, erhalte aber BadRequest. 

PostMan-Einstellungen:

 enter image description here

Chrome-Netzwerkpost-Details: enter image description here

Jetzt mache ich das in Android, scheitere aber:

Retrofit Service Interface:

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

Dies ist meine @ Background-Methode, um die Netzwerkanforderung mit dem obigen Dienst auszuführen

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

Hier ist meine Web-API-Methode:

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

Bitte helfen Sie mir, wo ich mich irre und wie ich das erreichen kann. Gibt es eine einfache Alternative zum Nachrüsten?

[Edit] Dieser Code funktioniert erfolgreich. Danke an 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

Ich hatte hier ein ähnliches Problem: Android mit Retrofit2 OkHttp3 - Multipart POST Fehler

Ich habe mein Problem gelöst, nachdem ich den Vorschlag von @TommySM genommen hatte. Wenn Ihnen noch unklar ist, denke ich, dass dies die Lösung ist:

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

Der wichtige Teil ist die Verwendung von MediaType.parse("text/plain") für MediaType des String-Parameters (ich glaube, Ihr Fall ist: folder & name -Parameter). okhttp3.MultipartBody.FORM ist ein Fehler.

Sehen Sie sich diese Screenshots zum Vergleich an:

1) Problematischer POST

 enter image description here

2) Korrigieren Sie den POST

 enter image description here

7
Shuwn Yuan Tee

Ich hoffe, es ist nicht zu spät, und wenn ja, dann könnte es jemand anderem helfen :) Meine 2 Cent darüber sind nach einer Weile das gleiche Problem:

Die Service-Definition, die nur @Part verwendet (ändern Sie die Feldnamen entsprechend den Erwartungen Ihres Servers)

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

Und für den Zaubertrick verweise ich auf beide Teile nur als RequestBody, benutze MediaType.parse() für jeden mit seinem eigenen Typ, stütze mich auf die @Multipart-Definition für die Anfrage selbst, keine Notwendigkeit für form-data-Sachen, dasselbe gilt für mehrere Dateien und mehrere Felder:

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

        }
    });
}

(Ich verwende einen Listener, um die Rückrufantwort an einen anderen Teil der App zurückzugeben (z. B. eine aufrufende Aktivität).).

Dies sendet definitiv die Datei (en) und das Textfeld, andere Probleme würden wahrscheinlich von der Serverseite stammen.

Hoffe das hilft!

6
TommySM

Bei Verwendung von Retrofit 2 müssen Sie entweder die RequestBody- oder die MultipartBody.Part-Klasse von OkHttp verwenden und Ihre Datei in einen Anforderungstext einkapseln. Schauen wir uns die Schnittstellendefinition für Dateiuploads an . Kennen Sie https://futurestud.io/blog/retrofit-2-how-to-upload-files-to-server

4
user3924438

Schnittstelle für Retrofit (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);
}

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

API-Aufruf von Anwendung

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

        }
    });
}

Fehler in Feldern wegen eines dieser Serverprobleme

  1. Google
  2. HTTP 500-Antwort mit Retrofit, aber Postanforderung über Postman gibt die gewünschte Antwort
  3. Wie bekomme ich eine Antwort-Retrofit-Ausnahme?
  4. SocketTimeoutException (und/oder http 500-Fehler) auf http-POST nachrüsten
1
shanraisshan

Es scheint, dass Ihre Service-Definition falsch ist. Versuchen

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

und

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

in Ihrer @Background-Methode. Dies alles setzt voraus, dass Sie Retrofit2 verwenden.

0
JohnWowUs