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這些字尾是獲取不到的:
下面為完整的上傳下載的Controller程式碼:@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(); }
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一下哦