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
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值