您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)SpringBoot中下載文件的方式有哪些,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
這里還是以GridFS為例,主要演示的還是從mongo下載下來(lái)的文件,如果是本地服務(wù)器上的文件,前端傳以文件路徑直接獲取流即可,如下:
InputStream in = new FileInputStream(System.getProperty("user.dir") + filePath);
接下來(lái)就演示下使用GridFsTemplate下載文件,mongo的配置其實(shí)上篇已經(jīng)貼過(guò)了,這里就直接貼代碼了,具體的就不做解釋了
@Service @Slf4j public 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<ServerAddress> serverAddresses = new ArrayList<>(); serverAddresses.add(new ServerAddress("10.1.61.101:27017")); // 連接認(rèn)證 MongoCredential credential = MongoCredential.createCredential("root", "admin", "Root_123".toCharArray()); MongoClientOptions.Builder builder = MongoClientOptions.builder(); //最大連接數(shù) builder.connectionsPerHost(10); //最小連接數(shù) builder.minConnectionsPerHost(0); //超時(shí)時(shí)間 builder.connectTimeout(1000*3); // 一個(gè)線程成功獲取到一個(gè)可用數(shù)據(jù)庫(kù)之前的最大等待時(shí)間 builder.maxWaitTime(5000); //此參數(shù)跟connectionsPerHost的乘機(jī)為一個(gè)線程變?yōu)榭捎玫淖畲笞枞麛?shù),超過(guò)此乘機(jī)數(shù)之后的所有線程將及時(shí)獲取一個(gè)異常.eg.connectionsPerHost=10 and threadsAllowedToBlockForConnectionMultiplier=5,最多50個(gè)線程等級(jí)一個(gè)鏈接,推薦配置為5 builder.threadsAllowedToBlockForConnectionMultiplier(5); //最大空閑時(shí)間 builder.maxConnectionIdleTime(1000*10); //設(shè)置池連接的最大生命時(shí)間。 builder.maxConnectionLifeTime(1000*10); //連接超時(shí)時(shí)間 builder.socketTimeout(1000*10); MongoClientOptions myOptions = builder.build(); MongoClient mongoClient = new MongoClient(serverAddresses, credential, myOptions); return mongoClient; } @Override protected String getDatabaseName() { return "notifyTest"; } /** * 獲取另一個(gè)數(shù)據(jù)庫(kù) * @return */ public String getFilesDataBaseName() { return "notifyFiles"; } /** * 用于切換不同的數(shù)據(jù)庫(kù) * @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; } /** * 獲取文件存儲(chǔ)模塊 * @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 轉(zhuǎn)為 GridFsResource * @param gridFsFile * @return */ public GridFsResource convertGridFSFile2Resource(GridFSFile gridFsFile) { GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFsFile.getObjectId()); return new GridFsResource(gridFsFile, gridFSDownloadStream); } }
對(duì)比上篇配置,新增加的兩個(gè)方法主要為了應(yīng)對(duì)SpringBoot2.x之后,GridFsTemplate的findOne()方法返回從GridFSDBFile改為GridFSFile,導(dǎo)致文件下載時(shí)不能使用以前的GridFSDBFile 操作流了,所以加了轉(zhuǎn)換操作
分別把兩種方式的下載實(shí)現(xiàn)貼出來(lái)
@RequestMapping(value = "/download2", method = RequestMethod.GET) public void downLoad2(HttpServletResponse response, String id) { userService.download2(response, id); }
controller層如上,只是測(cè)試所以很簡(jiǎn)略,因?yàn)槭橇鞯男问剿圆⒉恍枰付ㄝ敵龈袷?,下面看下service層實(shí)現(xiàn)
/** * 以O(shè)utputStream形式下載文件 * @param response * @param id */ @Override public void download2(HttpServletResponse response, String id) { GridFsTemplate gridFsTemplate = new GridFsTemplate(mongoConfig.getDbFactory(mongoConfig.getFilesDataBaseName()), mongoTemplate.getConverter()); // 由于springBoot升級(jí)到2.x 之后 findOne方法返回由 GridFSDBFile 變?yōu)?nbsp;GridFSFile 了,導(dǎo)致下載變得稍微有點(diǎn)繁瑣 GridFSFile gridFSFile = gridFsTemplate.findOne(new Query(Criteria.where("_id").is(id))); String fileName = gridFSFile.getFilename(); GridFsResource gridFsResource = mongoConfig.convertGridFSFile2Resource(gridFSFile); // 從此處開(kāi)始計(jì)時(shí) long startTime = System.currentTimeMillis(); InputStream in = null; OutputStream out = null; try { // 這里需對(duì)中文進(jìn)行轉(zhuǎn)碼處理 fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1"); // 告訴瀏覽器彈出下載對(duì)話框 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 ."); } } }
可以看到篇幅較長(zhǎng),注釋也已經(jīng)都在代碼里了
@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,標(biāo)明是以流的形式下載文件,下面看下service層
/** * 以ResponseEntity形式下載文件 * @param id * @return */ @Override public ResponseEntity<byte[]> download(String id) { GridFsTemplate gridFsTemplate = new GridFsTemplate(mongoConfig.getDbFactory(mongoConfig.getFilesDataBaseName()), mongoTemplate.getConverter()); // 由于springBoot升級(jí)到2.x 之后 findOne方法返回由 GridFSDBFile 變?yōu)?nbsp;GridFSFile 了,導(dǎo)致下載變得稍微有點(diǎn)繁瑣 GridFSFile gridFSFile = gridFsTemplate.findOne(new Query(Criteria.where("_id").is(id))); String fileName = gridFSFile.getFilename(); GridFsResource gridFsResource = mongoConfig.convertGridFSFile2Resource(gridFSFile); // 從此處開(kāi)始計(jì)時(shí) long startTime = System.currentTimeMillis(); try { InputStream in = gridFsResource.getInputStream(); // 請(qǐng)求體 byte[] body = IOUtils.toByteArray(in); // 請(qǐng)求頭 HttpHeaders httpHeaders = new HttpHeaders(); // 這里需對(duì)中文進(jìn)行轉(zhuǎn)碼處理 fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1"); // 告訴瀏覽器彈出下載對(duì)話框 httpHeaders.add("Content-Disposition", "attachment;filename=" + fileName); ResponseEntity<byte[]> 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工具類,依賴如下
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency>
經(jīng)過(guò)測(cè)試,當(dāng)文件小于1m內(nèi)兩種方式速度差不多,然后我測(cè)了5m的文件,結(jié)果如下:
可以看到OutputStream略慢一點(diǎn)點(diǎn),當(dāng)文件再大時(shí)這邊也并沒(méi)有作測(cè)試,總之本人推薦使用ResponseEntity形式下載文件~
如果只是想顯示某個(gè)路徑下的圖片而并不需要下載,那么采用如下形式:
@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()方法,該方法是返回輸入流中所包含的字節(jié)數(shù),方便在讀寫(xiě)操作時(shí)就能得知數(shù)量,能否使用取決于實(shí)現(xiàn)了InputStream這個(gè)抽象類的具體子類中有沒(méi)有實(shí)現(xiàn)available這個(gè)方法。
如果實(shí)現(xiàn)了那么就可以取得大小,如果沒(méi)有實(shí)現(xiàn)那么就獲取不到。
例如FileInputStream就實(shí)現(xiàn)了available方法,那么就可以用new byte[in.available()];這種方式。
但是,網(wǎng)絡(luò)編程的時(shí)候Socket中取到的InputStream,就沒(méi)有實(shí)現(xiàn)這個(gè)方法,那么就不可以使用這種方式創(chuàng)建數(shù)組。
關(guān)于“SpringBoot中下載文件的方式有哪些”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。