您的位置:首页 > 编程语言 > Java开发

java_web 学习记录(六):文件上传和下载

2017-04-19 15:44 691 查看
文件的上传和下载是web应用中很常见的功能,下面我们具体来看实例。

这里有两篇博客写的十分详细,大家可以移步学习:

http://www.cnblogs.com/xdp-gacl/p/4200090.html

http://www.cnblogs.com/lxnlxn/p/5843636.html

我这里主要是就用过的几种方法做个综合总结,加深理解。

一, 添加依赖包

<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
二,添加配置文件upload.properties
#文件上传目录
upload.path=/WEB-INF/upload
#文件缓存目录
temp.path=/WEB-INF/upload
#允许上传的文件类型
file.type=properties,log,jpg,png
#单个文件上传的大小,如果超过则会抛异常
file.size.max=1048576
#总文件上传的大小
size.max=10485760
#缓冲大小,超过则会存在缓冲文件夹中
size.threshold=102400
三,编写工具类,获取properties文件内容
package com.example.util;

import java.util.HashMap;
import java.util.Map;

public class PropertiesUtil {

private Map<String, String> properties = new HashMap<String, String>();

private PropertiesUtil() {}

private static PropertiesUtil instance = new PropertiesUtil();

public static PropertiesUtil getInstance() {
return instance;
}

public void addProperty(String propertyName, String propertyValue) {
properties.put(propertyName, propertyValue);
}

public String getProperty(String propertyName) {
return properties.get(propertyName);
}
}
四,编写监听器,在服务器启动时就去读取指定properties文件内容,封装到工具类中
package com.example.listener;

import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Properties;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import com.example.util.PropertiesUtil;

/**
* 监听器,初始化读取*.properties文件内容
* @author Administrator
*
*/
public class PropertiesListener implements ServletContextListener{

public void contextInitialized(ServletContextEvent sce) {
ClassLoader loader = this.getClass().getClassLoader();
//获取upload.properties文件内容
InputStream in = loader.getResourceAsStream("/upload.properties");
if (in != null) {
Properties prop = new Properties();
try {
prop.load(in);
Enumeration<?> e = prop.propertyNames();
while (e.hasMoreElements()) {
String key = (String) e.nextElement();
String value = prop.getProperty(key);
//将文件内容封装到PropertiesUtil对象中
PropertiesUtil.getInstance().addProperty(key, value);
}
} catch (IOException e) {
e.printStackTrace();
}

}

}

public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub

}

}

五,在web.xml中添加对监听器的配置

<!-- 监听器,读取配置文件 -->
<listener>
<listener-class>com.example.listener.PropertiesListener</listener-class>
</listener>
六,文件上传实例
1) 编写文件上传servlet类

package com.example.servlet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import com.example.util.PropertiesUtil;

public class FileuploadServlet extends HttpServlet{

private static final long serialVersionUID = 7223830312220318953L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
super.doGet(req, resp);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置输入输出编码
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");

//文件上传保存目录
String realPath = this.getServletContext().getRealPath(PropertiesUtil.getInstance().getProperty("upload.path"));

//上传时生成的临时文件保存目录
String tempPath = this.getServletContext().getRealPath(PropertiesUtil.getInstance().getProperty("temp.path"));
File file_temp = new File(tempPath);
if (!file_temp.exists()) {
file_temp.mkdirs();
}

DiskFileItemFactory factory = new DiskFileItemFactory();
//临时文件存放目录
factory.setRepository(file_temp);
//缓冲区大小,当文件大小超过设置的缓冲值时就会存储到临时目录中
factory.setSizeThreshold(Integer.parseInt(PropertiesUtil.getInstance().getProperty("size.threshold")));

//创建一个文件上传解析器
ServletFileUpload upload = new ServletFileUpload(factory);
//是否表单提交
if (!ServletFileUpload.isMultipartContent(request)) {
return;
}
//监听文件上传进度
upload.setProgressListener(new ProgressListener() {
public void update(long pBytesRead, long pContentLength, int arg2) {
System.out.println("文件大小为:" + pContentLength + ",当前已处理:" + pBytesRead);
}
});
//上传文件名编码设置
upload.setHeaderEncoding("utf-8");
//上传单个文件大小
upload.setFileSizeMax(Integer.parseInt(PropertiesUtil.getInstance().getProperty("file.size.max")));
//上传总文件大小
upload.setSizeMax(Integer.parseInt(PropertiesUtil.getInstance().getProperty("size.max")));

//表单提交支持多文件上传
try {
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
//获取表单的属性名字
String name = item.getFieldName();
//如果获取的 表单信息是普通的 文本 信息
if(item.isFormField()){
//获取提交的参数
String value = item.getString("utf-8") ;
System.out.println("表单参数:"+name+"="+value);
}else{
/**
* 以下三步,主要获取 上传文件的名字
*/
//获取上传文件路径名
String fileName = item.getName() ;
if(fileName==null || fileName.trim().equals("")){
continue;
}
//截取 上传文件的 字符串名字,加1是 去掉反斜杠,
fileName = fileName.substring(fileName.lastIndexOf("\\")+1);

//由于文件名有可能会重复,这里需要改一个唯一文件名,用时间戳方式
//先获取文件名,不含后缀
String preName = fileName.substring(0, fileName.lastIndexOf("."));
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
preName = preName + "-"+sdf.format(new Date());
//获取文件名后缀,作为存储目录
String fileType = fileName.substring(fileName.lastIndexOf(".")+1);

//获取到文件后缀后可以校验文件类型是否允许上传
String alownType = PropertiesUtil.getInstance().getProperty("file.type");
if (!alownType.contains(fileType)) {
response.getWriter().println("暂不支持该文件类型上传");
return;
}

String dir = realPath+"\\" + fileType;
//创建文件夹
File file_dir = new File(dir);
if (!file_dir.exists()) {
file_dir.mkdirs();
}

//文件目录
String url = dir+"\\"+preName+"."+ fileType;

//真正写到磁盘上
//获取输入流
InputStream in = item.getInputStream();
//创建输出流
OutputStream out = new FileOutputStream(url);
int length = 0;
byte[] buffer = new byte[1024];
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}

in.close();
out.flush();
out.close();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}

}

2) 在web.xml中添加映射:

<!-- 文件上传 -->
<servlet>
<servlet-name>FileuploadServlet</servlet-name>
<servlet-class>com.example.servlet.FileuploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FileuploadServlet</servlet-name>
<url-pattern>/upload</url-pattern>
</servlet-mapping>
3) 编写文件上传upload.html,表单提交
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/webDemo/upload" method="post" enctype="multipart/form-data">
<label>上传用户</label>
<input type="text" name="userName">
<br/><br/>
<label>上传文件</label>
<input type="file" name="file" />
<br/><br/>
<input type="submit" value="提交" />
</form>
</body>
</html>
这里需要注意,设置enctype="multipart/form-data",否则无法接收到文件

4)启动 测试:



查看控制台打印日志:

四月 20, 2017 11:30:58 上午 org.apache.tomcat.util.digester.SetPropertiesRule begin
警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.j2ee.server:webDemo' did not find a matching property.
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: Server version:        Apache Tomcat/8.0.38
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: Server built:          Oct 6 2016 20:51:55 UTC
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: Server number:         8.0.38.0
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: OS Name:               Windows 8.1
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: OS Version:            6.3
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: Architecture:          amd64
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: Java Home:             D:\installFile\JDK\jdk1.8.0_102\jre
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: JVM Version:           1.8.0_102-b14
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: JVM Vendor:            Oracle Corporation
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: CATALINA_BASE:         H:\project\.metadata\.plugins\org.eclipse.wst.server.core\tmp0
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: CATALINA_HOME:         H:\tools\mglinker-eclipse\apache-tomcat-8.0.38
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: Command line argument: -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:65153
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: Command line argument: -Dcatalina.base=H:\project\.metadata\.plugins\org.eclipse.wst.server.core\tmp0
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: Command line argument: -Dcatalina.home=H:\tools\mglinker-eclipse\apache-tomcat-8.0.38
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: Command line argument: -Dwtp.deploy=H:\project\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: Command line argument: -Djava.endorsed.dirs=H:\tools\mglinker-eclipse\apache-tomcat-8.0.38\endorsed
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.VersionLoggerListener log
信息: Command line argument: -Dfile.encoding=UTF-8
四月 20, 2017 11:30:58 上午 org.apache.catalina.core.AprLifecycleListener lifecycleEvent
信息: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: D:\installFile\JDK\jdk1.8.0_102\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;D:/installFile/JDK/jdk1.8.0_102/bin/../jre/bin/server;D:/installFile/JDK/jdk1.8.0_102/bin/../jre/bin;D:/installFile/JDK/jdk1.8.0_102/bin/../jre/lib/amd64;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;D:\installFile\JDK\jdk1.8.0_102\bin;D:\installFile\JDK\jdk1.8.0_102\jre\bin;;H:\tools\code-gen-eclipse\apache-maven-3.3.9\bin;D:\Program Files\MySQL\MySQL Utilities 1.3.5\;H:\tools\code-gen-eclipse\eclipse;;.
四月 20, 2017 11:30:58 上午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["http-nio-8088"]
四月 20, 2017 11:30:58 上午 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
信息: Using a shared selector for servlet write/read
四月 20, 2017 11:30:58 上午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["ajp-nio-8010"]
四月 20, 2017 11:30:58 上午 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
信息: Using a shared selector for servlet write/read
四月 20, 2017 11:30:58 上午 org.apache.catalina.startup.Catalina load
信息: Initialization processed in 1033 ms
四月 20, 2017 11:30:58 上午 org.apache.catalina.core.StandardService startInternal
信息: Starting service Catalina
四月 20, 2017 11:30:58 上午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet Engine: Apache Tomcat/8.0.38
四月 20, 2017 11:30:59 上午 org.apache.jasper.servlet.TldScanner scanJars
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
四月 20, 2017 11:30:59 上午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-nio-8088"]
四月 20, 2017 11:30:59 上午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["ajp-nio-8010"]
四月 20, 2017 11:30:59 上午 org.apache.catalina.startup.Catalina start
信息: Server startup in 1058 ms
文件大小为:66024,当前已处理:4096
文件大小为:66024,当前已处理:4096
文件大小为:66024,当前已处理:4096
文件大小为:66024,当前已处理:7592
文件大小为:66024,当前已处理:11646
文件大小为:66024,当前已处理:15700
文件大小为:66024,当前已处理:15784
文件大小为:66024,当前已处理:19838
文件大小为:66024,当前已处理:23892
文件大小为:66024,当前已处理:23976
文件大小为:66024,当前已处理:28030
文件大小为:66024,当前已处理:32084
文件大小为:66024,当前已处理:32168
文件大小为:66024,当前已处理:36222
文件大小为:66024,当前已处理:40276
文件大小为:66024,当前已处理:40360
文件大小为:66024,当前已处理:44414
文件大小为:66024,当前已处理:48468
文件大小为:66024,当前已处理:48552
文件大小为:66024,当前已处理:52606
文件大小为:66024,当前已处理:56660
文件大小为:66024,当前已处理:56744
文件大小为:66024,当前已处理:60798
文件大小为:66024,当前已处理:64852
文件大小为:66024,当前已处理:64936
文件大小为:66024,当前已处理:66024
表单参数:userName=aa
再到本地文件上传目录查看,已经成功上传文件。

文件上传需要注意几点:

1) 上传文件不要放到一个文件夹中,可以根据上传的文件进行分类,分开存储,

我这里是根据文件类型来存储的,实际业务中,可以根据功能模块等来分类

2) 上传的文件名有可能会重复,因此每次上传存储时需要给定一个唯一名进行存储,

这里使用的是时间戳,给文件重命名。

但是下载文件时,就需要再把文件名改回原先上传的名字,下面会演示

3) 实际项目中,在文件上传后,还需要将文件名,存储目录等信息备份到数据库,

方便下载时,根据文件目录直接查找目标文件

七,文件下载

在上篇中,我们简单演示了下文件下载的过程,就是将本地目标文件以流的形式直接输出到浏览器,并通知浏览器以附件形式下载。

这里我们演示的是,用户在浏览器端自主选择需要下载的文件。

1)  编写文件下载的servlet类

package com.example.servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.example.util.PropertiesUtil;

/**
* 文件下载
* 从服务器上获取资源写到客户本地
* 1,找到需要下载的文件
* 2,将文件以流的形式输出到客户端
*
*/
public class FiledownloadServlet extends HttpServlet {

private static final long serialVersionUID = -2006274549235667712L;

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//设置输入输出编码
request.setCharacterEncoding("utf-8");
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");

//需要下载的文件名
String fileName = request.getParameter("fileName");

//获取文件夹目录,这里与上传目录一致
String realPath = this.getServletContext().getRealPath(PropertiesUtil.getInstance().getProperty("upload.path"));
/*
* 获取文件目录
* 避免一个文件夹存储文件过多,且不方便管理,在上传文件的时候就制定规则生成相应文件夹,存储上传的文件
* 那么这里就需要根据存储规则找到文件目录
* 在文件上传时,是以文件名后缀作为存储文件夹的
*/
String dir = realPath + "\\" + fileName.substring(fileName.lastIndexOf(".")+1);
//先判断文件夹是否存在,如果不存在需要创建一个
File file_dir = new File(dir);
if (!file_dir.exists()) {
file_dir.mkdirs();
}
String filePath = dir +"\\" + fileName ;
//文件如果不存在,则返回页面提示
File file_load = new File(filePath);
if (!file_load.exists()) {
response.getWriter().println("资源不存在或已被删除");
return;
}

//真正返回的文件名应该去掉上传时添加的时间戳唯一标识
String preName = fileName.substring(0, fileName.lastIndexOf("-"));
fileName = preName + fileName.substring(fileName.lastIndexOf("."));
//设置content-disposition响应头控制浏览器以下载的形式打开文件
//设置了输出编码并不能保证下载后的文件名中文不会乱码,这里需要额外设置文件名编码,URLEncoder.encode(fileName,"UTF-8")
response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(fileName,"UTF-8"));

//创建输入流
InputStream in = new FileInputStream(filePath);
//获取输出流
OutputStream out = response.getOutputStream();

int length = 0;
byte[] buffer = new byte[1024];
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}

out.flush();
out.close();
in.close();

}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
super.doPost(req, resp);
}

}

2) 在web.xml中配置映射

<!-- 文件下载 -->
<servlet>
<servlet-name>FiledownloadServlet</servlet-name>
<servlet-class>com.example.servlet.FiledownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FiledownloadServlet</servlet-name>
<url-pattern>/download</url-pattern>
</servlet-mapping>

3) 编写download.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="http://localhost:8088/webDemo/download?fileName=springboot-demo-20170420140211.log" title="点击下载文件">springboot-demo-20170420140211.log</a>
<br/><br/>
<a href="http://localhost:8088/webDemo/download?fileName=2-20170420140229.png" title="点击下载图片">2-20170420140229.png</a>
</body>
</html>
4) 启动测试:



==============================================================

文件的上传和下载到此就告一段落了,这里是用的servlet实现的

当然也有很多其他方法可以实现,比如使用spring框架,在controller中实现

前端也有很多封装好的文件上传插件,比如bootstrap-fileinput

我们了解了原理,其他方法其实也是大同小异,以后有空再补充,这里就不多说了。

最后说明一下,这里上传的文件是放在WEN-INF目录下的,从浏览器无法直接访问,提高了文件的安全性

但是有时候我们需要直接在页面展示,比如图片等,那么上传目录就要改成webapp下,

例如我放一张图片test.png到目录/webapp/conf下面,在download.html中添加一行代码:

<a href="http://localhost:8088/webDemo/conf/test.png" title="点击查看图片">test.png</a>
那么点击链接,就能直接查看到图片了,而不用到后台查找了。

下篇我们来学习如何将数据导出为excel,java_web 学习记录(七):jxl excel export
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: