1. 程式人生 > >Java上傳下載完全解析(二)

Java上傳下載完全解析(二)

上一篇我們分析了Java Web中的上傳開發: Java上傳下載完全解析(一) ,今天我們研究一下Java Web的下載與檔案位置配置資訊在開發與生產環境中的切換方法。

一、檔案下載

  檔案下載其實非常簡單,首先我們根據請求的資訊得到檔案的名稱,然後根據檔案位置進行拼接得到具體的檔案路徑。然後我們只需要在HttpServletResponse中得到檔案的輸出流,並從檔案系統中讀取資訊放到輸出流即可。

  在工程中,首先我們會有一個檔案的基礎地址資訊,比如為/data/dev,那麼我在上傳一個xx.txt檔案時,上傳時我會先得到其後綴,然後生成一個隨機的檔名並加上其後綴,比如7eec46bcc65a21cbe293726eaa9175cf.txt,那麼其最終地址為/data/dev/7eec46bcc65a21cbe293726eaa9175cf.txt,這樣,上傳時就將其儲存到此路徑下。

  同樣的道理,下載的時候,需要告訴我需要下載的檔名為7eec46bcc65a21cbe293726eaa9175cf.txt,這樣就可以得到其路徑為/data/dev/7eec46bcc65a21cbe293726eaa9175cf.txt,然後就可以得到檔案流並進行下載了。

  還有一點需要注意的是,假如我們下載的時候,假設地址為http://localhost:8080/download/xxx.txt,那麼下載的地址字首即為http://localhost:8080/download/,所以在上傳的時候,得到檔名後,需要給客戶端返回此檔案的下載路徑,比如檔名為7eec46bcc65a21cbe293726eaa9175cf.txt,那麼返回給客戶端的下載路徑即為http://localhost:8080/7eec46bcc65a21cbe293726eaa9175cf.txt。

  下面貼程式碼,注意@RequestMapping("/download/{fileName:.+}")中最後必須加入.+,否則.txt .xml這些字尾是獲取不到的:

    @RequestMapping("/download/{fileName:.+}")
    public void download(@PathVariable String fileName, HttpServletRequest request, HttpServletResponse response) throws Exception   {
  
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        //獲取下載檔案露肩
        String downLoadPath = filePath + fileName;
        //獲取檔案的長度
        long fileLength = new File(downLoadPath).length();
        //設定檔案輸出型別
        response.setHeader("Content-disposition", "attachment; filename="
                + new String(fileName.getBytes("utf-8"), "ISO8859-1"));
        //設定輸出長度
        response.setHeader("Content-Length", String.valueOf(fileLength));
        //獲取輸入流
        bis = new BufferedInputStream(new FileInputStream(downLoadPath));
        //輸出流
        bos = new BufferedOutputStream(response.getOutputStream());
        byte[] buff = new byte[2048];
        int bytesRead;
        while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
            bos.write(buff, 0, bytesRead);
        }
        //關閉流
        bis.close();
        bos.close();
    }
下面為完整的上傳下載的Controller程式碼:
package com.happyheng.controller;

import com.happyheng.entity.response.FileUploadResponse;
import com.happyheng.utils.MD5Utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;

/**
 *
 */
@RestController
public class FileController {

    @Value("${file.path}")
    private String filePath;

    @Value("${file.download.path}")
    private String fileDownloadPath;


    @RequestMapping("test")
    public FileUploadResponse test(){
        FileUploadResponse uploadResponse = new FileUploadResponse();
        uploadResponse.setFilePath("test");
        return uploadResponse;
    }

    @ResponseBody
    @RequestMapping("/up")
    public FileUploadResponse upload(HttpServletRequest request) throws IllegalStateException, IOException, NoSuchAlgorithmException {

        String fileHttpPath = "";
        //建立一個通用的多部分解析器
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
        //判斷 request 是否有檔案上傳,即多部分請求
        if (multipartResolver.isMultipart(request)) {
            //轉換成多部分request
            MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
            //取得request中的所有檔名
            Iterator<String> iter = multiRequest.getFileNames();
            while (iter.hasNext()) {
                //記錄上傳過程起始時的時間,用來計算上傳時間
                int pre = (int) System.currentTimeMillis();
                //取得上傳檔案
                MultipartFile file = multiRequest.getFile(iter.next());
                if (file != null) {
                    //取得當前上傳檔案的檔名稱
                    String myFileName = file.getOriginalFilename();
                    //如果名稱不為空,說明該檔案存在,否則說明該檔案不存在
                    if (!myFileName.trim().isEmpty()) {
                        System.out.println(myFileName);

                        String fileName = getRondomFileName() + getFileType(myFileName);
                        //定義本地路徑
                        String path = filePath + fileName;
                        File localFile = new File(path);
                        file.transferTo(localFile);

                        fileHttpPath = fileDownloadPath + fileName;
                    }
                }
                //記錄上傳該檔案後的時間
                int finaltime = (int) System.currentTimeMillis();
                System.out.println(finaltime - pre);
            }

        }
        FileUploadResponse uploadResponse = new FileUploadResponse();
        uploadResponse.setFilePath(fileHttpPath);
        return uploadResponse;
    }

    @RequestMapping("/download/{fileName:.+}")
    public void download(@PathVariable String fileName, HttpServletRequest request, HttpServletResponse response) throws Exception {

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        //獲取下載檔案露肩
        String downLoadPath = filePath + fileName;
        //獲取檔案的長度
        long fileLength = new File(downLoadPath).length();
        //設定檔案輸出型別
        response.setHeader("Content-disposition", "attachment; filename="
                + new String(fileName.getBytes("utf-8"), "ISO8859-1"));
        //設定輸出長度
        response.setHeader("Content-Length", String.valueOf(fileLength));
        //獲取輸入流
        bis = new BufferedInputStream(new FileInputStream(downLoadPath));
        //輸出流
        bos = new BufferedOutputStream(response.getOutputStream());
        byte[] buff = new byte[2048];
        int bytesRead;
        while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
            bos.write(buff, 0, bytesRead);
        }
        //關閉流
        bis.close();
        bos.close();
    }


    private String getFileType(String fileName) {
        String[] splitText = fileName.split("[.]");
        if (splitText.length == 0) {
            return "";
        }
        return "." + splitText[splitText.length - 1];
    }

    private String getRondomFileName() throws NoSuchAlgorithmException {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmssSSS");
        StringBuilder builder = new StringBuilder(dateFormat.format(new Date()));
        builder.append((int) (Math.random() * 1000));

        return MD5Utils.getMD5(builder.toString());
    }

}


二、檔案位置配置資訊在開發與生產環境中的切換方法:

  我們在開發環境與生產環境中(為了簡單,只說了兩種環境),一般使用的檔案上傳下載路徑是不同的,打一個比方,在dev下,檔案的路徑為/data/dev,在prod環境下,檔案的路徑為/data/prod,那麼,怎麼在兩種環境中進行切換呢,我們可以使用PropertyPlaceholderConfigurer:

  首先在resources下面新建兩個.properties檔案。


其中dev下面檔案為:

# file storage
file.path=/data/prod


# file download path
file.download.path=http://localhost:8080/download/

prod下面檔案為:

# file storage
file.path=/data/prod


# file download path  
file.download.path=http://localhost:8080/download/

這樣,然後在servlet-context.xml中設定:

    <!-- 環境配置檔案掃描器 -->
    <beans profile="dev">
        <bean id="propertyConfigurer"
              class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:dev/path.properties</value>
                </list>
            </property>
        </bean>
    </beans>


    <beans profile="prod">
        <bean id="propertyConfigurerProd"
              class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:prod/path.properties</value>
                </list>
            </property>
        </bean>
    </beans>

這樣,我們在web.xml中初始化DispatcherServlet時,就可以指定環境了:

  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath*:servlet-context.xml</param-value>
    </init-param>
    <init-param>
      <param-name>spring.profiles.default</param-name>
      <param-value>dev</param-value>
    </init-param>
    <init-param>
      <param-name>spring.profiles.active</param-name>
      <param-value>prod</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  我們此時既指定了default環境,又指定了active環境,所以由於active環境優先順序更高,所以使用prod環境,所以我們檔案的下載路徑為file.path=/data/prod

三、

  此專案的完整地址為 FileService ,裡面會有上面原始碼中沒有的一些工具類,歡迎大家下載,如果感覺有幫助的話,也可以star一下哦