Android错误笔记---下载文件无法获取到文件(ContentLength)的大小

由于项目需要做APP自动更新的功能,即进入程序自动请求获取最新的APP版本号,判断是否需要更新,这里就要涉及到下载APP文件并显示安装界面提示用户安装程序,做过下载文件的同学都知道,为了提高用户体验,就要实现下载进度条的的功能,进度值一般都是0-100,而这个值都是经过代码计算的。首先请求下载文件时http响应中会返回ContentLength,即文件大小,我们再根据当前已下载的大小除以文件大小计算得到进度值。很简单的功能,在开发中却遇到很多问题,获取到的ContentLength值为-1。


使用的网络框架是retrofit+rxjava,下载文件的主要代码如下:

/**
     * 异步下载文件
     * @param baseUrl 地址
     * @param url 文件地址
     * @param path 存放路径
     * @param fileName 文件名
     * @param callback 回调
     */
    public void downloadFileWithDynamicUrlSync(String baseUrl, String url, final String path, final String fileName, final DownloadCallback callback) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.readTimeout(10, TimeUnit.SECONDS);
        builder.connectTimeout(9, TimeUnit.SECONDS);
        LoggingInterceptor httpLoggingInterceptor = null;
        if (BuildConfig.DEBUG) {
            //debug模式下   打印网络请求日志
            httpLoggingInterceptor = new LoggingInterceptor.Builder()
                    .loggable(BuildConfig.DEBUG)
                    .setLevel(Level.BASIC)
                    .log(Platform.INFO)
                    .request("请求")
                    .response("响应")
                    .build();
        }
        builder.addInterceptor(httpLoggingInterceptor);
        Retrofit retrofit = new Retrofit.Builder().baseUrl(baseUrl)
                .client(builder.build())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
        ConnectService service = retrofit.create(ConnectService.class);
        LogUtil.logNormalMsg("开始下载");
        service.downloadFileWithDynamicUrlSync(url).subscribeOn(Schedulers.io()).
                observeOn(Schedulers.io())
                .subscribe(new Observer<ResponseBody>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(ResponseBody responseBody) {
                        InputStream inputStream = responseBody.byteStream();
                        writeFile(inputStream, responseBody, path, fileName, callback);
                    }

                    @Override
                    public void onError(Throwable e) {
                        callback.downloadFail("文件下载失败:" + e.getMessage());
                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

    /**
     * 将输入流写入文件
     *
     * @param inputString 写入流
     * @param filePath    文件路径
     * @param fileName    文件名
     * @param callback    回调接口
     */
    private static void writeFile(InputStream inputString, ResponseBody body, String filePath, String fileName, DownloadCallback callback) {

        File file = new File(filePath + File.separator + fileName);
        if (file.exists()) {
            file.delete();
        }
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            byte[] b = new byte[1024];
            int len;
                long totalLength = body.contentLength();

            Log.logNormalMsg("ConnectControl", "totalLength:" + totalLength);
            long current = 0;
            while ((len = inputString.read(b)) != -1) {
                fos.write(b, 0, len);
                current += len;
                if (current != 0 && totalLength != 0) {
                    int progress = (int) (100 * current / totalLength);
                    callback.downloading(progress);
                    LogUtil.logNormalMsg("ConnectControl", "文件" + fileName + "已下载---" + (int) (100 * current / totalLength) + "%");
                }
            }
            inputString.close();
            fos.close();

        } catch (FileNotFoundException e) {
            LogUtil.logNormalMsg("文件写入失败---FileNotFoundException:" + e.getMessage());
            callback.downloadFail("文件下载失败");
        } catch (IOException e) {
            LogUtil.logNormalMsg("文件写入失败---IOException:" + e.getMessage());
            callback.downloadFail("文件下载失败");
        }
        callback.downloadSuccess(file.getAbsolutePath());
    }
    public void clear(){
        if (service != null) {
            service = null;
        }
    }

执行下载时获取的ContentLength为-1,网上搜索了下解决方法,都说设置请求头header的Accept-Encoding为identity就可以了,大概意思是设置返回的编码方式为本体,因为http会将大文件返回的编码方式默认为gzip,即压缩返回,这样就没法获取到ContentLength,接下来设置一下header头:

OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.readTimeout(10, TimeUnit.SECONDS);
        builder.connectTimeout(9, TimeUnit.SECONDS);
        builder.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request().newBuilder()
                        .addHeader("Accept-Encoding","identity").build();
                return chain.proceed(request);
            }
        });

执行请求,但是结果还是一样,ContentLength为-1,最后在这篇文章才找到答案
http 响应头里 没有 或者有 content-length 的几种可能性

由于后台是将文件读取为文件流再将文件流返回给前端,这样的http响应默认的Transfer-Encoding为chunked,当Connection为keep-alive时,Transfer-Encoding和ContentLength只能二选一存在一个,所有前端是没法获取到ContentLength的,如下图:
在这里插入图片描述
这里的解决方法就是后台设置ContentLength的值即可:

//file为下载的文件
response.setContentLength((int) file.length());

再执行请求后前端可以看到ContentLength:
在这里插入图片描述
最后在调试时还发现一个情况,当后台设置的下载方式不是以读取文件再将文件流的方式返给前端,而是直接返回文件在服务器上的绝对路径,即:

http://你的ip/group1/M00/00/7E/CgEoa1z3hDyAcUcEAMRHXV-xJ4g661.apk

前端直接根据这个路径下载就能获取到ContentLength的值,估计是http自动识别并返回了ContentLength。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android开发教程及笔记-完整版.pdf》是一本关于Android开发的教程和笔记的完整版电子书。这本电子书主要介绍了Android开发所需的各种知识和技术,对于想要学习Android开发的人来说是一本非常有价值的资料。 这本电子书包含了Android开发的基础知识,如Android系统的介绍、Android开发环境的搭建以及常用开发工具的使用方法。同时,它也详细介绍了Android应用程序的开发流程,包括界面设计、布局管理、事件处理、数据库操作等方面的内容,使读者能够全面掌握Android应用程序的开发技巧。 此外,这本电子书还介绍了一些高级的Android开发技术,如网络编程、多媒体处理、传感器应用等方面的知识。通过学习这些高级技术,读者可以进一步提升自己的Android开发水平,设计出更加优秀和复杂的Android应用程序。 除了知识点的介绍之外,这本电子书还提供了大量的实例和代码,让读者能够通过实践来巩固所学知识。同时,它还给出了一些常见问题的解决方法和开发经验的分享,帮助读者更好地理解和应用所学的知识。 总之,《Android开发教程及笔记-完整版.pdf》是一本非常实用的Android开发学习资料,其全面而详细的内容将帮助读者系统地学习和掌握Android开发的技能,为实际项目的开发提供了很好的指导。无论是初学者还是有一定经验的开发者,都可以从中受益匪浅。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值