【问题标题】:Retrofit 2 file down/upload改造 2 文件下载/上传
【发布时间】:2015-12-27 16:39:03
【问题描述】:

我正在尝试使用 Retrofit 2 下载/上传文件,但找不到任何有关如何执行此操作的教程示例。 我的下载代码是:

@GET("documents/checkout")
public Call<File> checkout(@Query(value = "documentUrl") String documentUrl, @Query(value = "accessToken") String accessToken, @Query(value = "readOnly") boolean readOnly);

Call<File> call = RetrofitSingleton.getInstance(serverAddress)
                .checkout(document.getContentUrl(), apiToken, readOnly[i]);
call.enqueue(new Callback<File>() {
        @Override
        public void onResponse(Response<File> response,
                Retrofit retrofit) {
            String fileName = document.getFileName();
            try {
                System.out.println(response.body());
                long fileLength = response.body().length();
                InputStream input = new FileInputStream(response.body());
                File path = Environment.getExternalStorageDirectory();
                File file = new File(path, fileName);
                BufferedOutputStream output = new BufferedOutputStream(
                        new FileOutputStream(file));
                byte data[] = new byte[1024];

                long total = 0;
                int count;
                while ((count = input.read(data)) != -1) {
                    total += count;
                    output.write(data, 0, count);
                }

                output.flush();

                output.close();
            } catch (IOException e) {
                String logTag = "TEMPTAG";
                Log.e(logTag, "Error while writing file!");
                Log.e(logTag, e.toString());
            }
        }
        @Override
        public void onFailure(Throwable t) {
            // TODO: Error handling
            System.out.println(t.toString());
        }
    });

我已经尝试过呼叫和呼叫,但似乎没有任何效果。

服务器端代码在正确设置标头和mime类型后将文件的字节写入HttpServletResponse的输出流。

我做错了什么?

最后是上传代码:

@Multipart
@POST("documents/checkin")
public Call<String> checkin(@Query(value = "documentId") String documentId, @Query(value = "name") String fileName, @Query(value = "accessToken") String accessToken, @Part("file") RequestBody file);

RequestBody requestBody = RequestBody.create(MediaType.parse(document.getMimeType()), file);

            Call<String> call = RetrofitSingleton.getInstance(serverAddress).checkin(documentId, document.getFileName(), apiToken, requestBody);
            call.enqueue(new Callback<String>() {
                @Override
                public void onResponse(Response<String> response, Retrofit retrofit) {
                    System.out.println(response.body());
                }

                @Override
                public void onFailure(Throwable t) {
                    System.out.println(t.toString());
                }
            });

谢谢!

编辑:

回答后,下载只会产生损坏的文件(没有@Streaming),上传也不会。当我使用上述代码时,服务器返回 400 错误。改成之后

RequestBody requestBody = RequestBody.create(MediaType.parse(document.getMimeType()), file);
            MultipartBuilder multipartBuilder = new MultipartBuilder();
            multipartBuilder.addFormDataPart("file", document.getFileName(), requestBody);

            Call<String> call = RetrofitSingleton.getInstance(serverAddress).checkin(documentId, document.getFileName(), apiToken, multipartBuilder.build());

,请求执行,但后端似乎没有收到文件。

后端代码:

@RequestMapping(value = "/documents/checkin", method = RequestMethod.POST)
public void checkInDocument(@RequestParam String documentId,
        @RequestParam String name, @RequestParam MultipartFile file,
        @RequestParam String accessToken, HttpServletResponse response)

我做错了什么?我能够通过 Apache HttpClient 使用纯 Java 的后端:

    MultipartEntityBuilder builder = MultipartEntityBuilder.create();
    builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
    builder.addBinaryBody("file", new File("E:\\temp\\test.jpg"));
    HttpEntity httpEntity = builder.build();
    System.out.println("HttpEntity " + EntityUtils.toString(httpEntity.));
    HttpPost httpPost = new HttpPost(uri);
    httpPost.setEntity(httpEntity);

编辑 v2

对于任何有兴趣的人,现在就上传和下载工作:这些是解决方案:

服务:

@GET("documents/checkout")
public Call<ResponseBody> checkout(@Query(value = "documentUrl") String documentUrl, @Query(value = "accessToken") String accessToken, @Query(value = "readOnly") boolean readOnly);

@Multipart
@POST("documents/checkin")
public Call<String> checkin(@Query(value = "documentId") String documentId, @Query(value = "name") String fileName, @Query(value = "accessToken") String accessToken, @Part("file") RequestBody file);

下载代码:

    Call<ResponseBody> call = RetrofitSingleton.getInstance(serverAddress)
                .checkout(document.getContentUrl(), apiToken, readOnly[i]);
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Response<ResponseBody> response,
                    Retrofit retrofit) {
                String fileName = document.getFileName();

                try {
                    File path = Environment.getExternalStorageDirectory();
                    File file = new File(path, fileName);
                    FileOutputStream fileOutputStream = new FileOutputStream(file);
                    IOUtils.write(response.body().bytes(), fileOutputStream);
                } catch (IOException e) {
                    Log.e(logTag, "Error while writing file!");
                    Log.e(logTag, e.toString());
                }
            }

            @Override
            public void onFailure(Throwable t) {
                // TODO: Error handling
                System.out.println(t.toString());
            }
        });

上传代码:

    Call<String> call = RetrofitSingleton
                    .getInstance(serverAddress).checkin(documentId,
                            document.getFileName(), apiToken,
                            multipartBuilder.build());
            call.enqueue(new Callback<String>() {
                @Override
                public void onResponse(Response<String> response,
                        Retrofit retrofit) {
                    // Handle response here
                }

                @Override
                public void onFailure(Throwable t) {
                    // TODO: Error handling
                    System.out.println("Error");
                    System.out.println(t.toString());
                }
            });

【问题讨论】:

  • 您收到了哪些日志消息。可以设置日志级别restAdapter.setLogLevel(LogLevel.FULL);
  • 通过 Retrofit.client().interceptors().add 添加日志后,问题似乎是 content-length 始终为 0 但我不知道为什么,文件存在于文件系统中.
  • 你能用Spring Server中的完整下载方法更新吗?谢谢
  • 我很高兴我们有这个代码/功能,但我们甚至应该使用改造来上传和下载文件吗?在上传的情况下,我观​​察到改造在发送到服务器之前将整个文件读入内存,现在如果用户选择一个大文件,那么在 Android 上很容易达到应用程序允许的最大堆。有没有人遇到过这个问题?还有其他上传方式吗?

标签: android download upload retrofit


【解决方案1】:

对于下载,你可以使用ResponseBody作为你的返回类型——

@GET("documents/checkout")
@Streaming
public Call<ResponseBody> checkout(@Query("documentUrl") String documentUrl, @Query("accessToken") String accessToken, @Query("readOnly") boolean readOnly);

您可以在回调中获取ResponseBody 输入流--

Call<ResponseBody> call = RetrofitSingleton.getInstance(serverAddress)
            .checkout(document.getContentUrl(), apiToken, readOnly[i]);

call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Response<ResponseBody> response,
                Retrofit retrofit) {
            String fileName = document.getFileName();
            try {
                InputStream input = response.body().byteStream();
                //  rest of your code

如果您的服务器正确处理多部分消息,您的上传乍一看还不错。它在工作吗?如果不是,您能解释一下故障模式吗?您也可以通过不使其成为多部分来简化。去掉@Multipart注解,将@Path转换成@Body——

@POST("documents/checkin")
public Call<String> checkin(@Query("documentId") String documentId, @Query("name") String fileName, @Query("accessToken") String accessToken, @Body RequestBody file);

【讨论】:

【解决方案2】:

我正在使用改造 2.0.0-beta2,但在使用多部分请求上传图片时遇到问题。我通过使用这个答案解决了它:https://stackoverflow.com/a/32796626/2915075

对我来说,关键是使用带有 MultipartRequestBody 的标准 POST 而不是 @Multipart 带注释的请求。

这是我的代码:

改造服务类

@POST("photo")
Call<JsonElement> uploadPhoto(@Body RequestBody imageFile, @Query("sessionId"));

活动中的使用,片段:

RequestBody fileBody = RequestBody.create(MediaType.parse("image/jpeg"), imageFile);
MultipartBuilder multipartBuilder = new MultipartBuilder();
multipartBuilder.addFormDataPart("photo", imageFile.getName(), fileBody);
RequestBody fileRequestBody = multipartBuilder.build();

//call
mRestClient.getRetrofitService().uploadProfilePhoto(fileRequestBody, sessionId);

【讨论】:

  • MultipartBuilder 现在是 okhttp3 中的 MultipartBody.Builder
【解决方案3】:

我有同样的问题,我找到了一个上传文件的解决方案,在这里描述 Is it possible to show progress bar when upload image via Retrofit 2

【讨论】:

    【解决方案4】:

    我也遇到了这个问题,这就是我尝试解决问题的方法(改造 2)

     //1. What We Need From Server ( upload.php Script )
        public class FromServer {
            String result;
        }
    
        //2. Which Interface To Communicate Our upload.php Script?
        public interface ServerAPI {
    
            @Multipart
            @POST("upload.php")//Our Destination PHP Script
            Call<List<FromServer>> upload(
                    @Part("file_name") String file_name,
                    @Part("file") RequestBody description);
    
             Retrofit retrofit =
                    new Retrofit.Builder()
                            .baseUrl("http://192.168.43.135/retro/") // REMEMBER TO END with /
                            .addConverterFactory(GsonConverterFactory.create())
                     .build();
        }
    
    
        //3. How To Upload
        private void upload(){
    
                ServerAPI api = ServerAPI.retrofit.create(ServerAPI.class);
    
                File from_phone = FileUtils.getFile(Environment.getExternalStorageDirectory()+"/aa.jpg"); //org.apache.commons.io.FileUtils
                RequestBody to_server = RequestBody.create(MediaType.parse("multipart/form-data"), from_phone);
    
                api.upload(from_phone.getName(),to_server).enqueue(new Callback<List<FromServer>>() {
                    @Override
                    public void onResponse(Call<List<FromServer>> call, Response<List<FromServer>> response) {
                        Toast.makeText(MainActivity.this, response.body().get(0).result, Toast.LENGTH_SHORT).show();
                    }
                    @Override
                    public void onFailure(Call<List<FromServer>> call, Throwable t) { }
                });
    
    
             }
    
    //4. upload.php
    <?php
    
        $pic = $_POST['file_name'];
    
        $pic = str_replace("\"", "", $pic); //REMOVE " from file name
        if(file_exists($pic)){unlink($pic);}
    
        $f = fopen($pic, "w");
        fwrite($f,$_POST['file']);
        fclose($f);
    
        $arr[] = array("result"=>"Done");
        print(json_encode($arr));
    ?>
    

    【讨论】:

      【解决方案5】:

      您可以参考Image Download using Retrofit 2.0的教程

      图片下载暂时可以参考以下功能:

      void getRetrofitImage() {
      
          Retrofit retrofit = new Retrofit.Builder()
                  .baseUrl(url)
                  .addConverterFactory(GsonConverterFactory.create())
                  .build();
      
          RetrofitImageAPI service = retrofit.create(RetrofitImageAPI.class);
      
          Call<ResponseBody> call = service.getImageDetails();
      
          call.enqueue(new Callback<ResponseBody>() {
              @Override
              public void onResponse(Response<ResponseBody> response, Retrofit retrofit) {
      
                  try {
      
                      Log.d("onResponse", "Response came from server");
      
                      boolean FileDownloaded = DownloadImage(response.body());
      
                      Log.d("onResponse", "Image is downloaded and saved ? " + FileDownloaded);
      
                  } catch (Exception e) {
                      Log.d("onResponse", "There is an error");
                      e.printStackTrace();
                  }
      
              }
      
              @Override
              public void onFailure(Throwable t) {
                  Log.d("onFailure", t.toString());
              }
          });
      }
      

      以下是使用 Retrofit 2.0 下载文件处理部分图片

      private boolean DownloadImage(ResponseBody body) {
      
          try {
              Log.d("DownloadImage", "Reading and writing file");
              InputStream in = null;
              FileOutputStream out = null;
      
              try {
                  in = body.byteStream();
                  out = new FileOutputStream(getExternalFilesDir(null) + File.separator + "AndroidTutorialPoint.jpg");
                  int c;
      
                  while ((c = in.read()) != -1) {
                      out.write(c);
                  }
              }
              catch (IOException e) {
                  Log.d("DownloadImage",e.toString());
                  return false;
              }
              finally {
                  if (in != null) {
                      in.close();
                  }
                  if (out != null) {
                      out.close();
                  }
              }
      
              int width, height;
              ImageView image = (ImageView) findViewById(R.id.imageViewId);
              Bitmap bMap = BitmapFactory.decodeFile(getExternalFilesDir(null) + File.separator + "AndroidTutorialPoint.jpg");
              width = 2*bMap.getWidth();
              height = 6*bMap.getHeight();
              Bitmap bMap2 = Bitmap.createScaledBitmap(bMap, width, height, false);
              image.setImageBitmap(bMap2);
      
              return true;
      
          } catch (IOException e) {
              Log.d("DownloadImage",e.toString());
              return false;
          }
      }
      

      我希望它会有所帮助。一切顺利。快乐编码:)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-01-31
        • 2018-12-21
        • 1970-01-01
        • 2016-10-11
        • 1970-01-01
        • 2017-09-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多