上一篇我们分析了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一下哦