web-dev-qa-db-ja.com

Retrofit 2は、2つの追加の個別の文字列パラメーターを持つファイルをアップロードできません

解決策が見つかるまで、可能な代替解決策の質問の下部にある編集を読む.

これは、POSTManを使用した2つのパラメーターを持つ成功した投稿ファイルです。私はレトロフィットで同じことをしようとしていますが、BadRequestを受け取ります。

PostMan設定:

enter image description here

Chromeネットワークの投稿の詳細:enter image description here

ここで、これをAndroidで実行していますが、失敗しています:

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

これは、上記のサービスが生成されたネットワーク要求を実行するための私の@Backgroundメソッドです

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

これが私の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");
    }

私が間違っている場所とこれを達成する方法を助けてください、レトロフィットの簡単な代替手段はありますか?

[編集]このコードは正常に機能しています。おかげで 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(...);
15
ahmadalibaloch

私はここで同様の問題に直面しました: Retrofit2 OkHttp3を搭載したAndroid-Multipart POST Error

@TommySMの提案を受けて問題を解決しました。それでも不明な場合は、これが解決策だと思います:

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

重要な部分は、StringパラメーターのMediaTypeMediaType.parse("text/plain")を使用することです(フォルダーと名前のパラメーターはそうだと思います)。_okhttp3.MultipartBody.FORM_の使用は間違いです。

比較のためにこれらのスクリーンショットを参照してください:

1)問題のあるPOST

enter image description here

2)正しいPOST

enter image description here

10
Shuwn Yuan Tee

だから、手遅れではないことを願っています-もしそうなら-それは他の誰かを助けるかもしれない:)これについての私の2セントはしばらく前に同じ問題を抱えた後です:

_@Part_のみを使用したサービス定義(サーバーの期待に応じてフィールド名を変更します)

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

そして、魔法のトリックのために、両方の部分をRequestBodyとして参照し、それぞれの型にMediaType.parse()を使用し、リクエスト自体の_@Multipart_定義に依存します、_form-data_の必要はなく、同じことが複数のファイル、および複数のフィールドで機能します。

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

        }
    });
}
_

(リスナーを使用して、アプリの別の部分(たとえば、呼び出しアクティビティ)にコールバック応答を返します)。

これは間違いなくファイルとテキストフィールドを送信しますが、他の問題はおそらくサーバー側に起因するでしょう。

お役に立てれば!

7
TommySM

Retrofit 2を使用するには、OkHttpのRequestBodyクラスまたはMultipartBody.Partクラスを使用し、ファイルをリクエスト本文にカプセル化する必要があります。ファイルのアップロードのインターフェース定義を見てみましょう。あなたは見ましたか https://futurestud.io/blog/retrofit-2-how-to-upload-files-to-server

4
user3924438

レトロフィット用インターフェース(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);
}

応答クラス(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呼び出し

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

        }
    });
}

これらのサーバーの問題のためにフィールドにエラーがあります

  1. Google
  2. 後付けを使用したHTTP 500応答、ただしPostmanを介したPost要求は必要な応答を提供
  3. 例外を後付けするために応答本文を取得する方法?
  4. http-POSTでのSocketTimeoutException(および/またはhttp 500エラー)の修正
1
shanraisshan

サービス定義が間違っているようです。試して

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

そして

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

@Backgroundメソッドで。これはすべて、Retrofit2を使用していることを前提としています。

0
JohnWowUs