首页 > 其他分享 >springboot下载文件 范围下载

springboot下载文件 范围下载

时间:2023-09-06 15:22:21浏览次数:45  
标签:文件 springboot range Content length split offset response 下载

springboot下载文件 范围下载

关键词:springboot,download,Range,Content-Range,Content-Length,http code 206 Partial Content

下载文件的一部分,我们在request header:Range 中指定要获取的文件的字节范围。要注意http response header:Content-Length 一定要与Range中所表示的获取bytes的范围长度相等。
如果Content-Length大于要Range所表示的bytes长度,那么客户端在发起请求后,下载完相应的Range范围的bytes后,连接不会自动关闭,直到连接超时,抛出异常。
如果Content-Length小于要Range所表示的bytes长度,那么发起请求后,只会获得Content-Length所指定的较小范围的bytes,导致获取的数据不完整。

controller:

    @SneakyThrows
    @GetMapping("/download")
    public void download(HttpServletResponse response,
                         @RequestParam String versionNum,
                         @RequestHeader(value = HttpHeaders.RANGE, required = false) String range) {
        if (StringUtils.isEmpty(versionNum)) {
            throw new ServiceException("版本号不能为空");
        }
        // jar包的上一级目录
        String canonicalPath = FileUtil.getCanonicalPath(new File(".."));
        String filePath = canonicalPath + "/device-upgrade-pack-repo/" + versionNum + "/" + versionNum + ".tar";
        long fileSize = FileUtil.file(filePath).length();

        // 获得文件流
        BufferedInputStream inputStream = FileUtil.getInputStream(filePath);

        // offset表示从哪个字节开始读取,length表示读取多少个字节
        long offset = 0;
        long length;
        // 如果range为空,那么就是读取整个文件
        if (StringUtils.isEmpty(range)) {
            length = fileSize;
        } else {
            // 根据range计算offset和length
            Pair<Long, Long> pair = getOffset(range);
            offset = pair.getLeft();
            length = pair.getRight();
            // length == -1 表示 end range 为最后一个字节
            if (length == -1) {
                length = fileSize - offset + 1;
            }
            // 跳过文件的offset个字节
            inputStream.skip(offset);
        }

        response.setContentType("application/octet-stream");
        response.setHeader("Content-disposition",
                "attachment;filename=" + versionNum + ".tar");
        response.setHeader("Content-Length", String.valueOf(length));
        response.setHeader("Content-Range", "bytes" + " " +
                offset + "-" + (offset + length - 1) + "/" + fileSize);
        response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
        ServletOutputStream os = response.getOutputStream();

        byte[] buf = new byte[1024];
        int bytesRead;
        // sum 是当前读取的字节数
        int sum = 0;
        while (sum < length && (bytesRead = inputStream.read(buf, 0, (length - sum) <= buf.length ? (int) (length - sum) : buf.length)) != -1) {
            os.write(buf, 0, bytesRead);
            sum += bytesRead;
        }

        inputStream.close();
        os.close();
    }

解析range:

    /**
     * left is offset, right is length
     *
     * @param range
     * @return
     */
    private Pair<Long, Long> getOffset(String range) {
        if (!range.contains("bytes=") || !range.contains("-")) {
            throw new ServiceException("range 格式错误");
        }
        range = range.substring(range.lastIndexOf("=") + 1).trim();
        String[] split = range.split("-");
        if (split.length == 0 || split.length > 2) {
            throw new ServiceException("range 格式错误");
        }
        if (split.length == 1) {
            if (range.startsWith("-")) {
                return Pair.of(0L, Long.parseLong(split[0]) + 1);
            } else {
                // -1 表示length无限制
                return Pair.of(Long.parseLong(split[0]), -1L);
            }
        } else {
            return Pair.of(Long.parseLong(split[0]), Long.parseLong(split[1]) - Long.parseLong(split[0]) + 1);
        }
    }

测试下载接口:

    @Test
    void downloadObject() {
        OkHttpClient httpClient = HttpUtil.getHttpClient();
        Request request = new Request.Builder()
                .get()
                .url("http://localhost:8081/device/version/download?versionNum=1.1.1.2023")
                .header("Range", "bytes=0-10")
                .build();

        try (Response response = httpClient.newCall(request).execute()) {
            InputStream inputStream = response.body().byteStream();
            byte[] buf = new byte[5];
            int readBytes;

            while ((readBytes = inputStream.read(buf)) != -1) {
                if (readBytes < buf.length) {
                    buf = Arrays.copyOf(buf, readBytes);
                }
                System.out.println(Arrays.toString(buf) + "   " + readBytes);
            }
            inputStream.close();
            response.close();
            System.out.println("end");
        } catch (IOException e) {
            throw new ServiceException("请求异常");
        }
    }


输出结果:
[49, 46, 49, 46, 49]   5
[46, 50, 48, 50, 51]   5
[46]   1
end

标签:文件,springboot,range,Content,length,split,offset,response,下载
From: https://www.cnblogs.com/keboom/p/17682378.html

相关文章

  • 001-FactoryTalk View studio 恢复mer文件
    mer文件是AllenBradleyPanelViewPlus系列触摸屏上的运行文件,一般情况,用户在RSVIewStudioME或FactoryTakViewStudioME系统下开发完成人机界面程序后,编译成可在触摸屏上运行的mer格式文件,上传到伸触摸屏内存供其运行。由于是编译后的运行格式,它并不含有开发项目的全部信息,但......
  • ajax下载二进制文件(导出Excel)
    varurl='http://127.0.0.1'; varxhr=newXMLHttpRequest(); xhr.open('GET',url,true);//也可以使用POST方式,根据接口 xhr.responseType="blob";//返回类型blob //定义请求完成的处理函数,请求前也可以增加加载框/禁用下载按钮逻辑 xhr.o......
  • 借助html2canvas下载图片,有滚动条的情况显示不全的问题
    我自己的遇到的情况是将页面的一个小窗口里边的内容生成图片下载,试了网上搜到的几个方法都没有生效,最后自己用了个取巧的方法:通过调整overflow-y来解决这个问题。downloadItem(){consttargetDom=document.getElementById(`image-${this.dialogNo}`)targetDo......
  • HashCat 下载安装
    介绍Hashcat是一款用于破解密码的工具,据说是世界上最快最高级的密码破解工具,支持LM哈希、MD5、SHA等系列的密码破解,同时也支持Linux、Mac、Windows平台。一、官网https://hashcat.net二、github项目仓库https://github.com/hashcat/hashcat三、源码安装源码可以在......
  • ​​Android平台GB28181历史视音频文件下载规范探讨及技术实现
    技术背景上篇blog,我们提到了Android平台GB28181历史视音频文件检索规范探讨及技术实现,文件检索后,GB28181平台侧,可以针对文件列表进行回放或下载操作,本文主要探讨视音频文件下载相关。规范解读视音频文件下载基本要求SIP服务器接收到媒体接收者发送的视音频文件下载请求后向媒体流......
  • SpringBoot3.x 框架搭建
    SpringBoot3.0.0已经发布一段时间了,越来越多的公司考虑将技术框架升级到最新版本,JDK也相应要求JDK17以上。对应SpringBoot2.x的版本,建议先升级到SpringBoot2.7.0,之后再升级到SpringBoot3.x。 gitee源码地址:https://gitee.com/yhc910/spring-boot-framework.git  ......
  • sysfs文件系统各子目录含义
    /sys/block块设备目录,目前此目录已指向到/sys/devices/中设备描述符链接文件/sys/bus设备按总线类型分层放置子目录,/sys/devices/中的所有设备都是链接于某种总线,bus子目录在每一种具体总线之下都可以找到对应每一个具体设备的描述符号链接/sys/class设备按功能分类放置子目录,/......
  • 百度开源上传下载组件
    ​ 需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制。 第一步:前端修改由于项目使用的是BJUI前端框架,并没有使用框架本身的文件上传控件,而使用的基于jQuery的Uploadify......
  • Python之断点续传下载及进度显示
    Python之断点续传下载及进度显示某日,因工作需要下载大量OSGB数据,下载链接来源于一个csv文件,于是解析了csv文件然后下载。为了提高下载效率及进度显示,写了一份脚本。环境python3.7requestscsv过程解析csv废话不多说,先上代码:deffetch_download_url(source):res......
  • git 删除远程仓库中的文件(夹)
    在开发过程中如果在编辑.gitignore时疏漏,导致本不应该上传至远程仓库的某个文件(夹)被提交,则可以使用如下方式解决(只是从远程仓库中删除,本地文件不受影响、不会被删除)预览想要删除的文件命令:gitrm-r-n--cached文件/文件夹名称由于增加了参数-n,此时只是预览涉及的文件,不会......