Retrofit 2 图片上传成功但图片上传为损坏的文件

Retrofit 2 image upload success but image uploads as corrupted file

事情是这样的,文件作为 "photo.png" 成功上传,但是当我从浏览器打开照片时,照片已损坏。当我下载照片在我的电脑本地打开时,照片损坏了。

不知道为什么。

改造终点如下:

@Multipart
@POST("alerts/{alertId}/photo/")
Call<Object> uploadPhotoStill(@Header("Authorization") String credentials, @Path("alertId") int alertId,
                              @Part("photo\"; filename=\"picture_taken.jpeg\" ") RequestBody photo);

我是这样使用它的:我正在抓取用相机对象拍照后返回的字节数组。

private void initCamera() {
    if (camera == null) {
        camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);

        Camera.Parameters params = camera.getParameters();
        params.setPictureFormat(ImageFormat.JPEG);

        camera.setParameters(params);
    }
}

/**
 * Called when image data is available after a picture is taken.
 * The format of the data depends on the context of the callback
 * and {@link Camera.Parameters} settings.
 *
 * @param data   a byte array of the picture data
 * @param camera the Camera service object
 */
@Override
public void onPictureTaken(byte[] data, Camera camera) {
    Log.d(TAG, "onPictureTaken() called with: " + "data = [" + Arrays.toString(data) + "], camera = [" + camera + "]");
    handleTakenStill(data);
}

private void handleTakenStill(byte[] data) {
    Log.d(TAG, "handleTakenStill() was called");

    RequestBody requestBody = RequestBody.create(MediaType.parse("image/jpeg"), data);

    havenApi.uploadPhotoStill(Utils.encodeUserCredentials(), getCurrentAlert().getId(), requestBody).enqueue(new Callback<Object>() {
        @Override
        public void onResponse(Response<Object> response, Retrofit retrofit) {
            if (response.isSuccess()) {
                Log.d(TAG, "handleTakenStill.onResponse success: " + response.body().toString());
            } else {
                Log.e(TAG, "handleTakenStill.onResponse error: " + response.message());
            }
        }

        @Override
        public void onFailure(Throwable t) {
            Utils.logOnFailureRequest(TAG, t);
        }
    });
}

上传照片后总是调用成功。但是照片总是上传损坏,我不知道为什么。

1) 按照评论中的说明检查照片类型。有时swith到JPG格式解决问题。

2) 尝试将文件或文件路径作为 RequestBody 的参数(RequestBode 对象的另一个构造函数)

我使用下面的代码没有问题,但是我将文件作为表单数据发送:

@Multipart
    @POST("/api/tour/upload")
    Call<MainFileModel> uploadTourFiles(@PartMap Map<String, RequestBody> params);


    File mainFile = new File(fileLoad.getFile());
    Map params = new Map<>();
    params.put("file\"; filename=\"" + mainFile.getName(),
      RequestBody.create(MediaType.parse("multipart/form-data"),                                 mainFile));; 


//And Make call here!

我从来没有让这个与改造一起工作。但我能够用 OkHttp 做到这一点。

如果您想知道解决方案是什么:

/**
 * Called when image data is available after a picture is taken.
 * The format of the data depends on the context of the callback
 * and {@link Camera.Parameters} settings.
 *
 * @param data   a byte array of the picture data
 * @param camera the Camera service object
 */
@Override
public void onPictureTaken(byte[] data, Camera camera) {
    Log.d(TAG, "onPictureTaken() called with: " + "data = [" + Arrays.toString(data) + "], camera = [" + camera + "]");
    handleTakenStill(data);
}

private void handleTakenStill(byte[] data) {
    Log.d(TAG, "handleTakenStill() was called");

    Observable.create(uploadPhoto(data))
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<com.squareup.okhttp.Response>() {
                @Override
                public void onCompleted() {
                    Log.d(TAG, "onCompleted() was called");
                }

                @Override
                public void onError(Throwable e) {
                    Utils.logOnFailureRequest(TAG, e);
                }

                @Override
                public void onNext(com.squareup.okhttp.Response response) {
                    if (response.isSuccessful()) {
                        Log.d(TAG, "handleTakenStill.onResponse success: " + response.body().toString());
                    } else {
                        Log.e(TAG, "handleTakenStill.onResponse error: " + response.message());
                    }
                }
            });
}

@NonNull
private Observable.OnSubscribe<com.squareup.okhttp.Response> uploadPhoto(final byte[] data) {
    return new Observable.OnSubscribe<com.squareup.okhttp.Response>() {
        @Override
        public void call(Subscriber<? super com.squareup.okhttp.Response> subscriber) {
            OkHttpClient client = new OkHttpClient();

            RequestBody requestBody = new MultipartBuilder().type(MultipartBuilder.FORM)
                    .addFormDataPart("camera", "picture_taken.jpg", RequestBody.create(MediaType.parse("image/jpeg"), data))
                    .build();

            Request request = new Request.Builder()
                    .url(url)
                    .post(requestBody)
                    .addHeader("content-type", "multipart/form-data")
                    .addHeader("authorization", Utils.encodeUserCredentials())
                    .addHeader("accept", "application/json")
                    .addHeader("cache-control", "no-cache")
                    .build();

            try {
                com.squareup.okhttp.Response response = client.newCall(request).execute();
                subscriber.onNext(response);
                subscriber.onCompleted();
                if (!response.isSuccessful()) {
                    subscriber.onError(new Exception("Error uploading photo"));
                }
            } catch (IOException e) {
                subscriber.onError(e);
            }
        }
    };
}

我也有这个问题。我没有使用 MultiPart 表单,而是为适合我的图像数据使用了一个简单的 RequestBody。我在 Android 项目中使用 Kotlin。

我的改造API:

interface FilesApi {
    @PUT("/files/attachments/{parentId}")
    fun uploadAttachment(
        @Path("parentId") parentId: String,
        @Header("Content-Type") contentType: String,
        @Header("Name") name: String,
        @Header("Description") description: String?,
        @Body image: RequestBody
    ): Single<Response<FileUploadResponse>>
}

正在创建请求对象:

imageData 变量是一个 ByteArray

val requestFile = imageData.toRequestBody(contentType.toMediaTypeOrNull(), 0, imageData.size)

return filesApi.uploadAttachment(parentId, contentType, name, description, requestFile)
            .map { response ->
                response.code()
                response.errorBody()
                response.body()?.Result
            }