2016-03-18 24 views
9

Przeczytaj edycję na dole pytania o możliwe alternatywne rozwiązanie, dopóki nie zostanie znalezione rozwiązanie.Retrofit 2 nie może załadować pliku z dwoma dodatkowymi osobnymi parametrami ciągów.

Jest to udany plik post z dwoma parametrami za pomocą POSTMan. Próbuję zrobić to samo z retrofitem, ale otrzymam BadRequest.

Ustawienia listonosz:

enter image description here

Chrome Network Szczegóły postu: enter image description here

Teraz oto jak robię to w Androidzie, ale braku:

Retrofit Interfejs serwisowy:

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

To mój @Background sposób uruchomić żądanie sieciowe z powyższej usługi generowane

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

Oto moja metoda Web API:

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

Proszę o pomoc, gdzie mylę się i jak to osiągnąć, czy jest jakaś łatwa alternatywa dla modernizacji?

[Edytuj] Kod ten działa skutecznie, dzięki 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(...); 
+0

miałem ten sam problem z Androidem, w celu być precyzyjne, z 4.1.1; Teraz, używając przykładu z jonem, problem został rozwiązany i teraz mogę bez problemu przesyłać pliki (obrazy w moim przypadku). Powinieneś opublikować edycję jonów jako odpowiedź (chyba). –

+0

Nie, to nie jest rozwiązanie problemu, ale alternatywa (unikanie problemu). – ahmadalibaloch

+0

to jest do bani. Czy kiedykolwiek znalazłeś rozwiązanie tego problemu? –

Odpowiedz

0

Wygląda na to, definicja usługa jest źle. Spróbuj

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

i

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

w swojej metodzie @Background. To wszystko zakłada, że ​​używasz Retrofit2.

+0

Brak powodzenia z powyższą korektą. Tak, to jest retrofit2. :( – ahmadalibaloch

+1

nie działa, ten wyjątek zgłasza: 'java.lang.IllegalArgumentException: @Partamenty parametrów za pomocą MultipartBody.Part nie może zawierać nazwy części w adnotacji." – Hesam

+0

Hesam, używasz @Part zamiast @Part ("file"). – Bakus123

2

Korzystając z Retrofit 2, należy użyć klas Okapt's RequestBody lub MultipartBody.Part i enkapsulować plik do treści żądania. Rzućmy okiem na definicję interfejsu do przesyłania plików. Czy widział https://futurestud.io/blog/retrofit-2-how-to-upload-files-to-server

+0

Tak powyższy kod w pytaniu został faktycznie rozpoczęty z tego samouczka i jest prawie identyczny, z wyjątkiem dodatkowych dwóch parametrów ciągów.Lubna biblioteka Ion działa świetnie z tymi samymi danymi i ustawieniami – ahmadalibaloch

1

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

Response Ćwiczenia (JsonResponse) wezwanie

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 z aplikacji

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

     } 
    }); 
} 

Błąd w polu, bo żadnej z tych kwestii serwerze

  1. Google
  2. HTTP 500 response using retrofit, but Post request through Postman gives desired response
  3. How to get response body to retrofit exception?
  4. Retrofit SocketTimeoutException (and/or http 500 error) on http-POST
+0

Sprawdzę to wkrótce i Prześlij opinię – ahmadalibaloch

6

więc nadzieję, że nie jest za późno, a jeśli tak - to mi GHT pomoc kogoś innego :) moje 2 grosze o tym jest przecież ten sam problem jakiś czas temu:

definicji usługi, wykorzystując @Part tylko (modyfikować nazwy pól zgodnie z tym co serwer oczekuje)

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

I dla magicznej sztuczki odwołuję się do obu części tak jak RequestBody, używając MediaType.parse() do każdego z własnym typem, bazując na definicji dla samego żądania, nie ma potrzeby na rzeczy, to samo działa dla wielu plików, i wiele pól:

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

     } 
    }); 
} 

(Używam odbiornika, aby zwrócić odpowiedź zwrotną do innej części aplikacji (np. działanie wywołujące)).

To zdecydowanie wysyła plik/i pole tekstowe, inne problemy prawdopodobnie wynikałyby po stronie serwera.

Mam nadzieję, że to pomoże!

+0

Twoje rozwiązanie działa najlepiej dla mnie.Miałem do czynienia podobny problem tutaj: http://stackoverflow.com/questions/43320205/android-with-retrofit2-okhttp3-multipart-post-error. Problem był z częścią ciała który jest String, naprawiono go za pomocą MediaType 'text/plain' jak zasugerowano:' textBody = RequestBody.create (okhttp3.MediaType.parse (TXT_PLAIN), String.valueOf (SettingsManager.getUserID())); ' –

+0

@shuwnyuantee Cieszę się, że mogłem pomóc, możesz również odnieść się do http://stackoverflow.com/questions/28172496/android-volley-how-to-isolate-requests-in-another-class/30604191#30604191 czysty sposób korzystania z interfejsu CustomEventListener (ogólny pomysł jest podobny między Volley i Retrofit) – TommySM

+0

fajne @TommySM, sprawdzi się na Volley :) –

3

ja obliczu podobnego problemu tutaj: Android with Retrofit2 OkHttp3 - Multipart POST Error

dostałem mój problem rozwiązany po zażyciu @TommySM sugestię. Jeśli nadal nie jest jasne dla ciebie, myślę, że jest to rozwiązanie:

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

Ważną częścią jest użycie MediaType.parse("text/plain") dla MediaType parametru String (wierzę sprawa jest folder & nazwa parametru), stosując okhttp3.MultipartBody.FORM jest błąd.

Zobacz te zdjęcia dla porównania:

1) problematyczne STWÓRZ

enter image description here

2) Prawidłowe POST

enter image description here

+0

Dziękuję. Takie informacje są naprawdę przydatne. –

+0

Wygląda dobrze, spróbuję. – ahmadalibaloch