【问题标题】:Unable to upload video using Retrofit, possible fix?无法使用改造上传视频,可能的修复?
【发布时间】:2017-05-05 08:09:21
【问题描述】:

我正在尝试使用Retrofit 作为多部分表单数据将视频上传到服务器,并且我使用的是Retrofit 版本:2.02

compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'

这是 API 客户端类的代码:

ApiClient.java

    public class ApiClient {

    public static final String BASE_URL = "https://api.sproutvideo.com";
    private static Retrofit retrofit = null;


    public static Retrofit getClient() {
        if (retrofit==null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }
}

这是API接口的代码:

ApiInterface.java

    public interface ApiInterface {
    @Multipart
    @POST("/v1/videos")
    Call<SproutReply> pushVideo (@Header("SproutVideo-Api-Key") String key, @Part("file\"; filename=\"pp.mp4") RequestBody file);
}

我的要求是上传使用Intent.ACTION_PICK 挑选的视频。

这就是我调用 API 的方式:

 private void makeRequest(Uri uri)
{


   try {
       String path = uri.toString();
       Log.e("PATH", path);
       URI ss = new URI(uri.toString());
       file = new File(ss);

   }
   catch (Exception e){}


    RequestBody video = RequestBody.create(MediaType.parse("video/mp4"),file);
    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    ApiInterface apiService =
            ApiClient.getClient().create(ApiInterface.class);

    Call<SproutReply> call = apiService.pushVideo(KEY,video);
    call.enqueue(new Callback<SproutReply>()
    {
        @Override
        public void onResponse(Call<SproutReply> call, Response<SproutReply> response)
        {
            try
            {
                Log.e("TAG",""+response.body().toString());




            }
            catch (Exception e)
            {
                Toast.makeText(getActivity(), "Check data connection", Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onFailure(Call<SproutReply> call, Throwable t)
        {
            // Log error here since request failed
            Log.e("FAILURE", t.toString());
        }
    });


}

在这一行调用NullPointerExceptioncontent == null

RequestBody video = RequestBody.create(MediaType.parse("video/mp4"),file);

但是在这一行中,当我将 Uri(Log.e("PATH", path);) 记录为字符串时,我获得的值为:

content://com.android.providers.media.documents/document/video%3A75406

使用这个 Uri 我也可以在 VideoView 中播放视频,但是在 Retrofit 中它似乎崩溃了,可能是什么原因造成的以及如何解决?

这里也是 logcat:

java.lang.NullPointerException: content == null
                                                             at okhttp3.RequestBody.create(RequestBody.java:103)
                                                             at com.example.demovid.UploadFragment.makeRequest(UploadFragment.java:91)
                                                             at com.example.demovid.UploadFragment.onEvent(UploadFragment.java:133)
                                                             at java.lang.reflect.Method.invoke(Native Method)
                                                             at org.greenrobot.eventbus.EventBus.invokeSubscriber(EventBus.java:485)
                                                             at org.greenrobot.eventbus.EventBus.postToSubscription(EventBus.java:416)
                                                             at org.greenrobot.eventbus.EventBus.postSingleEventForEventType(EventBus.java:397)
                                                             at org.greenrobot.eventbus.EventBus.postSingleEvent(EventBus.java:370)
                                                             at org.greenrobot.eventbus.EventBus.post(EventBus.java:251)
                                                             at com.example.demovid.MainActivity.onActivityResult(MainActivity.java:67)
                                                             at android.app.Activity.dispatchActivityResult(Activity.java:6456)
                                                             at android.app.ActivityThread.deliverResults(ActivityThread.java:3729)
                                                             at android.app.ActivityThread.handleSendResult(ActivityThread.java:3776)
                                                             at android.app.ActivityThread.-wrap16(ActivityThread.java)
                                                             at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1412)
                                                             at android.os.Handler.dispatchMessage(Handler.java:102)
                                                             at android.os.Looper.loop(Looper.java:148)
                                                             at android.app.ActivityThread.main(ActivityThread.java:5461)
                                                             at java.lang.reflect.Method.invoke(Native Method)
                                                             at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                             at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

【问题讨论】:

标签: java android retrofit multipartform-data retrofit2


【解决方案1】:

问题在于您有一个内容 URI 而不是文件路径。当你将它传递给File 构造函数时,它会抛出一个FileNotFoundException,你会用

默默地忽略它
`catch (Exception e){}`

您可以使用 --

获取数据的文件描述符,而不是使用文件
ParcelFileDescriptor fd = getActivity().getContentResolver().openFileDescriptor(uri, "r");

并使用它来构建您的RequestBody。没有开箱即用的方法可以从描述符中创建一个,但是编写一个例程来完成它是相当容易的——

public static RequestBody createBody(final MediaType contentType, final ParcelFileDescriptor fd) {
    if (fd == null) throw new NullPointerException("content == null");

    return new RequestBody() {
        @Override public MediaType contentType() {
            return contentType;
        }

        @Override public long contentLength() {
            return fd.getStatSize();
        }

        @Override public void writeTo(BufferedSink sink) throws IOException {
            Source source = null;
            try {
                source = Okio.source(new ParcelFileDescriptor.AutoCloseInputStream(fd));
                sink.writeAll(source);
            } finally {
                Util.closeQuietly(source);
            }
        }
    };
}

然后像 --

一样使用它
RequestBody video = RequestBody.createBody(MediaType.parse("video/mp4"), fd);

我还注意到您正在对媒体类型进行硬编码,您也可以通过调用 getType 从内容解析器获取类型

【讨论】:

  • 感谢您的回答,但是现在我确实收到了411 的响应代码作为响应。那么,如何确认改造确实找到了要上传的视频呢? ://
  • @superman 嗯,这意味着它需要一个有效的内容长度。不幸的是,因为对于视频,您不想仅仅为了获得大小而将其读入内存。您将需要使用openFileDescriptor 而不是openInputStream。它只是更多的代码。编辑即将推出
  • 好的,感谢您的支持:)
  • sproutvideo.com/docs/api.html#Uploadanewvideo 这是我要上传的端点的文档!
  • 当然。它用于使用必要的方法创建RequestBody。你也可以创建一个匿名类。 contentType() 只是模仿你指定的内容类型,contentLength() 通过文件描述符获取文件的长度,writeTo() 创建 source 可用于将文件字节复制到 okhttp 的 Sink希望您向其中写入字节。它是根据您之前尝试使用的 create 方法设计的 -- github.com/square/okhttp/blob/master/okhttp/src/main/java/…
猜你喜欢
  • 2016-02-14
  • 2019-10-08
  • 2014-06-23
  • 2023-03-13
  • 1970-01-01
  • 2018-12-20
  • 2020-08-29
  • 2016-01-12
  • 1970-01-01
相关资源
最近更新 更多