千家信息网

SpringBoot中下载文件的方式有哪些

发表于:2025-11-18 作者:千家信息网编辑
千家信息网最后更新 2025年11月18日,这篇文章将为大家详细讲解有关SpringBoot中下载文件的方式有哪些,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。文件来源这里还是以GridFS为例,主要演示的还
千家信息网最后更新 2025年11月18日SpringBoot中下载文件的方式有哪些

这篇文章将为大家详细讲解有关SpringBoot中下载文件的方式有哪些,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

文件来源

这里还是以GridFS为例,主要演示的还是从mongo下载下来的文件,如果是本地服务器上的文件,前端传以文件路径直接获取流即可,如下:

InputStream in = new FileInputStream(System.getProperty("user.dir") + filePath);

接下来就演示下使用GridFsTemplate下载文件,mongo的配置其实上篇已经贴过了,这里就直接贴代码了,具体的就不做解释了

@Service@Slf4jpublic class MongoConfig extends AbstractMongoConfiguration {     @Autowired    private MongoTemplate mongoTemplate;    @Autowired    private GridFSBucket gridFSBucket;     @Override    public MongoClient mongoClient() {        MongoClient mongoClient = getMongoClient();        return mongoClient;    }     public MongoClient getMongoClient() {        // MongoDB地址列表        List serverAddresses = new ArrayList<>();        serverAddresses.add(new ServerAddress("10.1.61.101:27017"));        // 连接认证        MongoCredential credential = MongoCredential.createCredential("root", "admin", "Root_123".toCharArray());        MongoClientOptions.Builder builder = MongoClientOptions.builder();         //最大连接数        builder.connectionsPerHost(10);        //最小连接数        builder.minConnectionsPerHost(0);        //超时时间        builder.connectTimeout(1000*3);        // 一个线程成功获取到一个可用数据库之前的最大等待时间        builder.maxWaitTime(5000);        //此参数跟connectionsPerHost的乘机为一个线程变为可用的最大阻塞数,超过此乘机数之后的所有线程将及时获取一个异常.eg.connectionsPerHost=10 and threadsAllowedToBlockForConnectionMultiplier=5,最多50个线程等级一个链接,推荐配置为5        builder.threadsAllowedToBlockForConnectionMultiplier(5);        //最大空闲时间        builder.maxConnectionIdleTime(1000*10);        //设置池连接的最大生命时间。        builder.maxConnectionLifeTime(1000*10);        //连接超时时间        builder.socketTimeout(1000*10);         MongoClientOptions myOptions = builder.build();        MongoClient mongoClient = new MongoClient(serverAddresses, credential, myOptions);        return mongoClient;    }     @Override    protected String getDatabaseName() {        return "notifyTest";    }     /**     * 获取另一个数据库     * @return     */    public String getFilesDataBaseName() {        return "notifyFiles";    }     /**     * 用于切换不同的数据库     * @return     */    public MongoDbFactory getDbFactory(String dataBaseName) {        MongoDbFactory dbFactory = null;        try {            dbFactory = new SimpleMongoDbFactory(getMongoClient(), dataBaseName);        } catch (Exception e) {            log.error("Get mongo client have an error, please check reason...", e.getMessage());        }        return dbFactory;    }     /**     * 获取文件存储模块     * @return     */    public GridFsTemplate getGridFS() {        return new GridFsTemplate(getDbFactory(getFilesDataBaseName()), mongoTemplate.getConverter());    }     @Bean    public GridFSBucket getGridFSBuckets() {        MongoDatabase db = getDbFactory(getFilesDataBaseName()).getDb();        return GridFSBuckets.create(db);    }     /**     * 为了解决springBoot2.0之后findOne方法返回类更改所新增 将GridFSFile 转为 GridFsResource     * @param gridFsFile     * @return     */    public GridFsResource convertGridFSFile2Resource(GridFSFile gridFsFile) {        GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFsFile.getObjectId());        return new GridFsResource(gridFsFile, gridFSDownloadStream);    }}

对比上篇配置,新增加的两个方法主要为了应对SpringBoot2.x之后,GridFsTemplate的findOne()方法返回从GridFSDBFile改为GridFSFile,导致文件下载时不能使用以前的GridFSDBFile 操作流了,所以加了转换操作

文件下载

分别把两种方式的下载实现贴出来

1、OutputStream形式

@RequestMapping(value = "/download2", method = RequestMethod.GET)    public void downLoad2(HttpServletResponse response, String id) {        userService.download2(response, id);    }

controller层如上,只是测试所以很简略,因为是流的形式所以并不需要指定输出格式,下面看下service层实现

/**     * 以OutputStream形式下载文件     * @param response     * @param id     */    @Override    public void download2(HttpServletResponse response, String id) {        GridFsTemplate gridFsTemplate = new GridFsTemplate(mongoConfig.getDbFactory(mongoConfig.getFilesDataBaseName()), mongoTemplate.getConverter());        // 由于springBoot升级到2.x 之后 findOne方法返回由 GridFSDBFile 变为 GridFSFile 了,导致下载变得稍微有点繁琐        GridFSFile gridFSFile = gridFsTemplate.findOne(new Query(Criteria.where("_id").is(id)));        String fileName = gridFSFile.getFilename();        GridFsResource gridFsResource = mongoConfig.convertGridFSFile2Resource(gridFSFile);        // 从此处开始计时        long startTime = System.currentTimeMillis();        InputStream in = null;        OutputStream out = null;        try {            // 这里需对中文进行转码处理            fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");            // 告诉浏览器弹出下载对话框            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);            byte[] buffer = new byte[1024];            int len;            // 获得输出流            out = response.getOutputStream();            in = gridFsResource.getInputStream();            while ((len = in.read(buffer)) > 0) {               out.write(buffer, 0 ,len);            }        } catch (IOException e) {            log.error("transfer in error .");        } finally {            try {                if (null != in)                    in.close();                if (null != out)                    out.close();                log.info("download file with stream total time : {}", System.currentTimeMillis() - startTime);            } catch (IOException e){                log.error("close IO error .");            }        }    }

可以看到篇幅较长,注释也已经都在代码里了

2、ResponseEntity形式

@RequestMapping(value = "/download", method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)    public Object downLoad(String id) {        return userService.download(id);    }

controller需要指定输出格式application/octet-stream,标明是以流的形式下载文件,下面看下service层

/**     * 以ResponseEntity形式下载文件     * @param id     * @return     */    @Override    public ResponseEntity download(String id) {        GridFsTemplate gridFsTemplate = new GridFsTemplate(mongoConfig.getDbFactory(mongoConfig.getFilesDataBaseName()), mongoTemplate.getConverter());        // 由于springBoot升级到2.x 之后 findOne方法返回由 GridFSDBFile 变为 GridFSFile 了,导致下载变得稍微有点繁琐        GridFSFile gridFSFile = gridFsTemplate.findOne(new Query(Criteria.where("_id").is(id)));        String fileName = gridFSFile.getFilename();        GridFsResource gridFsResource = mongoConfig.convertGridFSFile2Resource(gridFSFile);        // 从此处开始计时        long startTime = System.currentTimeMillis();        try {            InputStream in = gridFsResource.getInputStream();            // 请求体            byte[] body = IOUtils.toByteArray(in);            // 请求头            HttpHeaders httpHeaders = new HttpHeaders();            // 这里需对中文进行转码处理            fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");            // 告诉浏览器弹出下载对话框            httpHeaders.add("Content-Disposition", "attachment;filename=" + fileName);            ResponseEntity responseEntity = new ResponseEntity<>(body, httpHeaders, HttpStatus.OK);            log.info("download file total with ResponseEntity time : {}", System.currentTimeMillis() - startTime);            return responseEntity;        } catch (IOException e) {            log.error("transfer in error .");        }        return null;    }

上面用到了IOUtils工具类,依赖如下

    commons-io    commons-io    2.4

两种方式下载速度比较

经过测试,当文件小于1m内两种方式速度差不多,然后我测了5m的文件,结果如下:

可以看到OutputStream略慢一点点,当文件再大时这边也并没有作测试,总之本人推荐使用ResponseEntity形式下载文件~

后话

如果只是想显示某个路径下的图片而并不需要下载,那么采用如下形式:

@RequestMapping(value = "/application/file/show", method = RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)    public Object downloadFile(@RequestParam("path") String filePath) {        try {            InputStream in = new FileInputStream(System.getProperty("user.dir") + filePath);            byte[] bytes = new byte[in.available()];            in.read(bytes);            return bytes;        } catch (IOException e) {            log.error("transfer byte error");            return buildMessage(ResultModel.FAIL, "show pic error");        }    }

需要注意上述的available()方法,该方法是返回输入流中所包含的字节数,方便在读写操作时就能得知数量,能否使用取决于实现了InputStream这个抽象类的具体子类中有没有实现available这个方法。

如果实现了那么就可以取得大小,如果没有实现那么就获取不到。

例如FileInputStream就实现了available方法,那么就可以用new byte[in.available()];这种方式。

但是,网络编程的时候Socket中取到的InputStream,就没有实现这个方法,那么就不可以使用这种方式创建数组。

关于"SpringBoot中下载文件的方式有哪些"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

文件 方法 形式 方式 最大 时间 线程 数据 数据库 篇文章 测试 输出 配置 繁琐 上篇 代码 只是 对话框 文件下载 更多 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 vb数据库登录界面怎么做 内蒙古经济网络技术有限公司 国家网络安全产业园电话 河南正规软件开发特点 小学生网络安全的简笔画 数据库中两个表写成一个类 软件开发10个常识 深梦互联网科技公司 共享足疗仪系统软件开发 滨海新区电子网络技术售后服务 软件开发接单技能 网络安全读研学什么 我是网络安全小卫士主题手抄报 郑州交友软件开发大概要多少钱 控制系统软件开发与设计 代理服务器选项在哪里 如何查服务器cpu的架构 以太网连接不到服务器 蜀山区正规网络技术咨询 未来之役黑屏连接不到服务器 如何使服务器网卡指示灯亮 网络安全教育平台福建省. 动态网络技术包括 常州服务器回收 数据库管理系统的安全性为 对数据库的要求是 软件开发 css文件 服务器硬盘坏了怎么换盘 怎么给5岁幼儿讲网络安全 空间数据库常用的模型
0