深入springMVC源碼------文件上傳源碼解析(下篇)


在上篇《深入springMVC------文件上傳源碼解析(上篇) 》中,介紹了springmvc文件上傳相關。那么本篇呢,將進一步介紹springmvc 上傳文件的效率問題。

相信大部分人在處理文件上傳邏輯的時候會直接獲取輸入流直接進行操作,偽代碼類似這樣:

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public ResultView upload(@RequestParam("file") MultipartFile file) {
    Inputstream in = file.getInputStream();
    ...         
}

但是,出於效率,其實我個人更推薦使用 MultipartFile 的 transferTo 方法進行操作,類似這樣:

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public ResultView upload(@RequestParam("file") MultipartFile file) {
    file.transferTo(new File(destFile));
    ...         
}

為什么呢?這個就得從源碼說起,廢話不多說,咱們直接去看源碼吧:

1. 先看 MultipartFile(其實現類CommonsMultipartFile) 的getInputStream方法:

CommonsMultipartFile:

public InputStream getInputStream() throws IOException {
        if (!isAvailable()) {
            throw new IllegalStateException("File has been moved - cannot be read again");
        }
        InputStream inputStream = this.fileItem.getInputStream();
        return (inputStream != null ? inputStream : new ByteArrayInputStream(new byte[0]));
    }

通過源碼可以看到,spring是通過commons-fileupload 中的FileItem對象去獲取輸入流,那么就去看看FileItem(其實現類DiskFileItem)的對應方法:

DiskFileItem:

public InputStream getInputStream()
        throws IOException {
        if (!isInMemory()) {
            return new FileInputStream(dfos.getFile());
        }

        if (cachedContent == null) {
            cachedContent = dfos.getData();
        }
        return new ByteArrayInputStream(cachedContent);
    }

通過源碼可以看到:先去查看是否存在於內存中,如果存在,就將內存中的file對象包裝為文件流, 如果不存在,那么就去看緩存,如果緩存存在就從緩存中獲取字節數組並包裝為輸入流。

 

接下來,咱們再看看 CommonsMultipartFile 的 transferTo 方法,以便形成比較:

CommonsMultipartFile:

@Override
    public void transferTo(File dest) throws IOException, IllegalStateException {
        if (!isAvailable()) {
            throw new IllegalStateException("File has already been moved - cannot be transferred again");
        }

        if (dest.exists() && !dest.delete()) {
            throw new IOException(
                    "Destination file [" + dest.getAbsolutePath() + "] already exists and could not be deleted");
        }

        try {
            this.fileItem.write(dest);
            if (logger.isDebugEnabled()) {
                String action = "transferred";
                if (!this.fileItem.isInMemory()) {
                    action = isAvailable() ? "copied" : "moved";
                }
                logger.debug("Multipart file '" + getName() + "' with original filename [" +
                        getOriginalFilename() + "], stored " + getStorageDescription() + ": " +
                        action + " to [" + dest.getAbsolutePath() + "]");
            }
        }
        catch (FileUploadException ex) {
            throw new IllegalStateException(ex.getMessage());
        }
        catch (IOException ex) {
            throw ex;
        }
        catch (Exception ex) {
            logger.error("Could not transfer to file", ex);
            throw new IOException("Could not transfer to file: " + ex.getMessage());
        }
    }

不多說,主要看 this.fileItem.write(dest) 這一句,利用commons-fileupload 中的相關方法:

DiskFileItem:

public void write(File file) throws Exception {
        if (isInMemory()) {
            FileOutputStream fout = null;
            try {
                fout = new FileOutputStream(file);
                fout.write(get());
            } finally {
                if (fout != null) {
                    fout.close();
                }
            }
        } else {
            File outputFile = getStoreLocation();
            if (outputFile != null) {
                // Save the length of the file
                size = outputFile.length();
........

通過源碼可以看到 transfoTo 方法很干凈利落,直接去將內存中的文件通過輸出流寫出到指定的file 。 等等,跟上面的 getInputStream方法相比,是不是省了點步驟? 是的,再來一張圖,清晰地表示兩個方法地不同之處:

圖中:

紅色線表示使用的是transferTo方法,黑色線代表getInputStream方法, 可見,transferTo直接將內存中的文件緩存直接寫入到磁盤的物理文件, 而getInputStream方法會中轉一次(先通過getInputStream從內存中獲取流,再通過outputStream輸出到磁盤物理文件)。兩者相比,即使從步驟來看,你也能看出來transferTo效率更高了吧。

好啦,本篇就到此結束啦!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM