programing

이미지를 포함한 Retrofit 2.0을 사용한 POST 다중 부품 폼 데이터

newstyles 2023. 8. 2. 08:54

이미지를 포함한 Retrofit 2.0을 사용한 POST 다중 부품 폼 데이터

Retrofit 2.0을 사용하여 서버에 HTTP POST를 수행하려고 합니다.

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

서버에서 파일이 올바르지 않다는 오류를 반환합니다.

iOS에서 (다른 라이브러리를 사용하여) 동일한 형식의 파일을 업로드하려고 했지만 성공적으로 업로드되기 때문에 이상합니다.

Retrofit 2.0을 사용하여 이미지를 업로드하는 올바른 방법이 무엇인지 궁금합니다.

업로드하기 전에 디스크에 저장해야 합니까?

추신: 저는 이미지가 포함되지 않은 다른 멀티파트 요청에 대해 개조를 사용했고 성공적으로 완료되었습니다.문제는 제가 신체에 바이트를 포함시키려고 할 때입니다.

Retrofit 2와 함께 해킹 없이 파일 이름을 업로드하는 올바른 방법이 있습니다.

API 인터페이스 정의:

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

다음과 같은 파일 업로드:

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

, . 동일한 방법으로 다른 매개 변수를 추가할 수도 있습니다.@Part 주석

1.9와 2.0 모두에서 솔루션을 강조하고 있는데, 이는 일부 사용자에게 유용하기 때문입니다.

1.9하고 다음과 file로 이 더

RetroFit 1.9

(서버 측 구현에 대해 잘 모르겠습니다) 이와 유사한 API 인터페이스 방법이 있습니다.

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

그리고 이렇게 사용합니다.

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

RetroFit 2의 경우 다음 방법을 사용합니다.

RetroFit 2.0(이것은 RetroFit 2의 문제에 대한 해결 방법이었으며, 현재 수정되었습니다. 올바른 방법은 jimmy0251의 답변을 참조하십시오.)

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

다음과 같이 사용:

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

레지스터 사용자를 위해 Retrofit 2.0을 사용하여 멀티파트/폼 전송 레지스터 계정에서 파일 이미지와 텍스트를 보냅니다.

내 등록 활동에서 비동기 작업 사용

//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) {..}

그리고 내 Register.java 클래스에서는 동기식 호출과 함께 Retrofit을 사용합니다.

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

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

유틸리티의 경우 r 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;
}
}

Retrofit 2.0에서 이미지 파일 업로드를 위한 코드 업데이트

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

MediaType.parse("image/*")MediaType.parse("image/jpeg")

RequestBody reqFile = RequestBody.create(MediaType.parse("image/jpeg"),
                                         file);
RequestBody email = RequestBody.create(MediaType.parse("text/plain"),
                                       "upload_test4@gmail.com");
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();

    }
});

그래서 이것은 여러분의 임무를 달성하는 아주 간단한 방법입니다.다음 단계를 수행해야 합니다.

첫걸음

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

은 전체 를 전체통화다해합니야다로 해야 .@Multipart request.item그리고.image number는 그냥 에싸 있문본뿐문입다니일로 싸인 문자열 입니다.RequestBody우리는 사용합니다.MultipartBody.Part class시할 수 .

두 번째 단계

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

이제 당신은image path로 변환해야 합니다.file.이제 변환file안으로RequestBody사용법RequestBody.create(MediaType.parse("multipart/form-data"), file)이제 변환해야 합니다.RequestBody requestFile안으로MultipartBody.Part사용법MultipartBody.Part.createFormData("Image", file.getName(), requestBody);.

ImageNumber그리고.ItemId서버로 보내야 하는 또 다른 데이터이기 때문에 저도 두 가지를 모두 만들고 있습니다.RequestBody.

자세한 내용은

@insomniac이 제공한 답변에 추가.다음을 생성할 수 있습니다.Map에 대한 매개 변수를 지정합니다.RequestBody이미지를 포함합니다.

인터페이스 코드

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

자바 클래스의 코드

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

확장 메서드를 MediaType, RequestBodyRequestBody로 사용하면 kotlin은 매우 간단합니다. 예를 들어 다음과 같습니다.

여기에 pdf 파일과 멀티파트를 사용한 이미지 파일과 함께 두 개의 일반 필드를 게시합니다.

Retrofit을 사용한 API 선언입니다.

    @Multipart
    @POST("api/Lesson/AddNewLesson")
    fun createLesson(
        @Part("userId") userId: RequestBody,
        @Part("LessonTitle") lessonTitle: RequestBody,
        @Part pdf: MultipartBody.Part,
        @Part imageFile: MultipartBody.Part
    ): Maybe<BaseResponse<String>>

그리고 이것을 실제로 부르는 방법은 다음과 같습니다.

api.createLesson(
            userId.toRequestBody("text/plain".toMediaType()),
            lessonTitle.toRequestBody("text/plain".toMediaType()),
            startFromRegister.toString().toRequestBody("text/plain".toMediaType()),
            MultipartBody.Part.createFormData(
                "jpeg",
                imageFile.name,
                imageFile.asRequestBody("image/*".toMediaType())
            ),
            MultipartBody.Part.createFormData(
                "pdf",
                pdfFile.name,
                pdfFile.asRequestBody("application/pdf".toMediaType())
            )
* Return MultipartBody from file path

 public static MultipartBody.Part generateFileBody(String imagePath)
    {
        File file = new File(imagePath);
        RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
        return MultipartBody.Part.createFormData("mediaProfilePic", file.getName(), requestFile);
    }

Retrofit을 사용하여 파일을 업로드하는 것은 매우 간단합니다. 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);

}

위의 코드 이미지에 키 이름이 있으므로 php를 사용하는 경우 $_FILES['image']['tmp_name']를 입력하여 이를 얻을 수 있습니다.그리고 filename="myfile.jpg"는 요청과 함께 전송되는 파일의 이름입니다.

이제 파일을 업로드하려면 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;
}

이제 아래 코드를 사용하여 파일을 업로드할 수 있습니다.

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

자세한 내용은 이 Retrofit Upload File 튜토리얼을 참조하십시오.

Kotlin 버전 및 사용률 감소를 위한 업데이트 포함RequestBody.create:

인터페이스 개조

@Multipart
@POST("uploadPhoto")
fun uploadFile(@Part file: MultipartBody.Part): Call<FileResponse>

및 업로드 대상

fun uploadFile(fileUrl: String){
    val file = File(fileUrl)
    val fileUploadService = RetrofitClientInstance.retrofitInstance.create(FileUploadService::class.java)
    val requestBody = file.asRequestBody(file.extension.toMediaTypeOrNull())
    val filePart = MultipartBody.Part.createFormData(
        "blob",file.name,requestBody
    )
    val call = fileUploadService.uploadFile(filePart)

    call.enqueue(object: Callback<FileResponse>{
        override fun onFailure(call: Call<FileResponse>, t: Throwable) {
            Log.d(TAG,"Fckd")
        }

        override fun onResponse(call: Call<FileResponse>, response: Response<FileResponse>) {
            Log.d(TAG,"success"+response.toString()+" "+response.body().toString()+"  "+response.body()?.status)
        }

    })
}

@jimmy0251 덕분에

requestBody는 업로드에 사용할 수 있습니다.

  val body: RequestBody = Builder().setType(MultipartBody.FORM)
        .addFormDataPart(
            "file", "<image name you wish to give>",
            RequestBody.create(
                MediaType.parse("application/octet-stream"),
                File(path)
            )
        )
        .build()
uploadProfilePhoto(body)

그러면 다음과 같이 전화합니다.

   @POST("/**")
    suspend fun uploadProfilePhoto(
        @Body body: RequestBody,
    ): ResponseBody
}

함수 이름여러 매개 변수를 사용하지 말고 코드의 가독성을 높일있는 간단한의 변수 규칙만 사용하면 됩니다. 이를 위해 다음과 같이 할 수 있습니다.

// MultipartBody.Part.createFormData("partName", data)
Call<SomReponse> methodName(@Part MultiPartBody.Part part);
// RequestBody.create(MediaType.get("text/plain"), data)
Call<SomReponse> methodName(@Part(value = "partName") RequestBody part); 
/* for single use or you can use by Part name with Request body */

// add multiple list of part as abstraction |ease of readability|
Call<SomReponse> methodName(@Part List<MultiPartBody.Part> parts); 
Call<SomReponse> methodName(@PartMap Map<String, RequestBody> parts);
// this way you will save the abstraction of multiple parts.

Retrofit을 사용하는 동안 발생할 수 있는 여러 예외가 있을 수 있습니다. 모든 예외코드로 문서화되어 있습니다. 두 가지 기능을 수행할 수 있습니다.parseParameterAnnotation그리고.parseMethodAnnotation예외를 적용할 수 있는 경우 이 과정을 거치면 구글/스택 오버플로보다 시간이 많이 절약됩니다.

저의 경우 PDF 파일을 보내야 했습니다.application/pdf), JSON 정보와 함께 (application/json감사하게도, Retrofit2는 이것을 매우 간단하게 만듭니다.

내 인터페이스는 다음과 같습니다.

interface MyApi {
    @Multipart
    @POST("upload")
    fun uploadPDF(
        @Part file: MultipartBody.Part,
        @Part(value = "jsoninfo") jsoninfo: MyJsonObject
    ): Call<MyResponse>
}

어디에jsoninfo내 JSON 데이터의 이름입니다.MyJsonObject이고, 데터클입다니스래이다▁my입니다.MyResponse물론 제가 기대하는 반응입니다.

그런 다음, 저는 제 API 메서드를 다음과 같이 부릅니다.

val myJsonObject = MyJsonObject(...)

// "file" is of type byte[] already
val requestBody = RequestBody.create(file, MediaType.parse("application/pdf"))
val filePart = MultipartBody.Part.createFormData("file", "myfile.pdf", requestBody)

api.uploadPDF(filePart, myJsonObject).enqueue(...)

언급URL : https://stackoverflow.com/questions/34562950/post-multipart-form-data-using-retrofit-2-0-including-image