SpringMVC 檔案下載之 --- IO與NIO實現及其效能比較
阿新 • • 發佈:2019-01-02
我的Controller類:FileController.java
package aboo.controller; import aboo.bean.FileInfo; import aboo.service.FileService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.util.FileCopyUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.commons.CommonsMultipartFile; import javax.servlet.http.*; import java.io.*; import java.nio.*; import java.nio.channels.FileChannel; import java.util.List; /** * 檔案的Controller * Created by admin on 2017/5/16. * * @author Aboo * */ @Api(value = "檔案的controller") @Controller @RequestMapping("/file") public class FileController { private Logger log = LoggerFactory.getLogger(FileController.class); @Autowired private FileService fs; /** * 根據檔案id下載檔案 (IO) * @param id 檔案id,從url中取得 * @param request * @param response * @throws IOException 下載檔案過程中,IO操作出現異常時,丟擲異常 */ @ApiOperation(value = "下載檔案",notes = "根據url裡檔案id來下載檔案") @RequestMapping(value="/download/{id}", method = RequestMethod.GET) public void download(@PathVariable("id") Long id,HttpServletRequest request,HttpServletResponse response) throws IOException { if (id != null && fs.exists(id)) { FileInfo fileInfo = fs.findById(id); String filename = fileInfo.getFile_name(); String realPath = request.getServletContext().getRealPath("WEB-INF/Files/"); File file = new File(realPath, filename); if (file.exists()) { response.setContentType(fileInfo.getMime_type());// 設定Content-Type為檔案的MimeType response.addHeader("Content-Disposition", "attachment;filename=" + filename);// 設定檔名 response.setContentLength((int) fileInfo.getLength()); //JAVA IO InputStream inputStream = new BufferedInputStream(new FileInputStream(file)); //Copy bytes from source to destination(outputstream in this example), closes both streams. FileCopyUtils.copy(inputStream, response.getOutputStream()); log.debug("download succeed! ---"+filename); } }else{ throw new Error("file who's id="+id+" is not exist!"); } } /** * 根據檔案id下載檔案 (NIO) * @param id 檔案id,從url中取得 * @param request * @param response * @throws IOException 下載檔案過程中,IO操作出現異常時,丟擲異常 */ @ApiOperation(value = "下載檔案",notes = "根據url裡檔案id來下載檔案") @RequestMapping(value="/nioDownload/{id}", method = RequestMethod.GET) public void nioDownload(@PathVariable("id") Long id,HttpServletRequest request,HttpServletResponse response) throws IOException { if (id != null && fs.exists(id)) { FileInfo fileInfo = fs.findById(id); String filename = fileInfo.getFile_name(); String realPath = request.getServletContext().getRealPath("WEB-INF/Files/"); File file = new File(realPath, filename); if (file.exists()) { response.setContentType(fileInfo.getMime_type());// 設定Content-Type為檔案的MimeType response.addHeader("Content-Disposition", "attachment;filename=" + filename);// 設定檔名 response.setContentLength((int) fileInfo.getLength()); //NIO 實現 int bufferSize = 131072; FileInputStream fileInputStream = new FileInputStream(file); FileChannel fileChannel = fileInputStream.getChannel(); // 6x128 KB = 768KB byte buffer ByteBuffer buff = ByteBuffer.allocateDirect(786432); byte[] byteArr = new byte[bufferSize]; int nRead, nGet; try { while ((nRead = fileChannel.read(buff)) != -1) { if (nRead == 0) { continue; } buff.position(0); buff.limit(nRead); while (buff.hasRemaining()) { nGet = Math.min(buff.remaining(), bufferSize); // read bytes from disk buff.get(byteArr, 0, nGet); // write bytes to output response.getOutputStream().write(byteArr); } buff.clear(); log.debug("download succeed! ---"+filename); } } catch (IOException e) { e.printStackTrace(); } finally { buff.clear(); fileChannel.close(); fileInputStream.close(); } } }else{ throw new Error("file who's id="+id+" is not exist!"); } } }
我的實體類:FileInfo.java
package aboo.bean; import javax.persistence.*; import java.io.Serializable; /** * 檔案資訊的實體類,與資料庫表tab_file對應 * Created by admin on 2017/5/9. * @author Aboo * @see java.io.Serializable * */ @Entity @Table(name = "tab_file") public class FileInfo implements Serializable{ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; //自增id private String file_name; private long last_modified; private String extension; private long length; private String mime_type; public FileInfo() { } /** * FileInfo的帶參構造器,由於id為自動生成的型別,在此不接受此引數 * * @param file_name 檔名 * @param last_modified 最後修改時間 * @param extension 副檔名 * @param length 檔案長度(單位:byte) * @param mime_type 檔案的Mime型別 */ public FileInfo(String file_name, long last_modified, String extension, long length, String mime_type) { this.file_name = file_name; this.last_modified = last_modified; this.extension = extension; this.length = length; this.mime_type = mime_type; } public Long getId() { return id; } public String getFile_name() { return file_name; } public void setFile_name(String file_name) { this.file_name = file_name; } public long getLast_modified() { return last_modified; } public void setLast_modified(long last_modified) { this.last_modified = last_modified; } public String getExtension() { return extension; } public void setExtension(String extension) { this.extension = extension; } public String getMime_type() { return mime_type; } public void setMime_type(String mime_type) { this.mime_type = mime_type; } public long getLength() { return length; } public void setLength(long length) { this.length = length; } @Override public String toString() { return "FileInfo{" + "id=" + id + ", file_name='" + file_name + '\'' + ", last_modified=" + last_modified + ", extension='" + extension + '\'' + ", length=" + length + ", mime_type='" + mime_type + '\'' + '}'; } }
我的JUnit Test:TestFileController.java
@WebAppConfiguration @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext.xml","classpath:dispatcher-servlet.xml"}) public class FileControllerTest { private MockMvc mockMvc; @Autowired private WebApplicationContext wac; private FileController fc; @Before public void setup(){ mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); fc = this.wac.getBean(FileController.class); } @Test public void testDownload() throws Exception{ MockHttpServletRequest request = new MockMultipartHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); fc.download(95L,request,response); System.out.println("tested download ...1"); } @Test public void testNioDownload() throws Exception{ MockHttpServletRequest request = new MockMultipartHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); fc.nioDownload(95L,request,response); System.out.println("test nio download ---"); } @Test public void CompareTime() throws Exception{ MockHttpServletRequest request = new MockMultipartHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); long a = System.currentTimeMillis(); fc.download(95L,request,response); long b = System.currentTimeMillis(); long c = System.currentTimeMillis(); fc.nioDownload(95L,request,response); long d = System.currentTimeMillis(); System.out.println("io download takes :"+(b-a)); System.out.println("nio download takes :"+(d-c)); } }
我寫了CompareTime() 方法,比較IO 和 NIO 所消耗的時間:
執行結果如下:
最後,僅僅從時間上考慮的話,NIO的優勢相當驚人的!!!