1. 程式人生 > >SpringMVC 檔案下載之 --- IO與NIO實現及其效能比較

SpringMVC 檔案下載之 --- IO與NIO實現及其效能比較

我的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的優勢相當驚人的!!!