[转]java web 文件上传
实现WEB开发中的文件上传功能,需完成如下二步操作:
在WEB页面中添加上传输入项,<input type=“life” name=“”>,使用时注意:
1. 必须要设置input输入项的name属性,否则浏览器将不会发送上传文件的数据。
2. 必须把input项的enctype属值设为multipart/form-data.设置该值后,浏览器在上传文件时,将把文件数据附带在http请求消息体中,并使用MIME协议对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。
如何在Servlet中读取文件上传数据,并保存到服务器本地硬盘中?
Request对象提供了一个getInputStream()方法,使用这个方法可以获取浏览器提交过来的输入流。但如果浏览器上传多个文件时,我们应该如何区分开来?这是一项复杂的工作。
为了方便用户处理文件上传数据,Apache开源组织提供了用于处理上面应用的开源组件——Commons-fileupload。这个组件使用简单,性
能也比较优异,所以几乎都使用它来实现上传文件的处理功能。Struts的上传文件处理部分也是使用它实现的。
Fileupload组件工作流程:
WEB服务器
request
ServletFil
eupLoad
DiskFileItem
Factory
代表普通字段的
FileItem
代表上传文件1
FileItem
代表上传文件2
FileItem
isFileForm
getFieldName
getString
getInputStream
getName
getInputStream
getName
核心API-DiskFileItemFactory:
DiskFileItemFactory是创建FileItem对象的工厂,这个工厂常用方法:
1. public DiskFileItemFactory(int sizeThreshold, java.io.File repository),常用的构造函数。
2. public void setSizeThreshold(int sizeThreshold),设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件。
3. public void setRepository(java.io.File repository),指定临时文件目录,默认值为System.getProperty("java.io.tmpdir")。
核心API-ServletFileupLoad:
ServletFileUpload 负责处理上传的文件数据,并将表单中每个输入项封装到一个FileItem对象中。常用方法有:
1. boolean isMultipartContent(HttpServletRequest request),判断上传表单是否为上传表单类型。
2. List parseRequest(HttpServletRequest request),解析request对象,并把表单中的每一个输入项包装到一个fileItem 对象中,并返回一个保存了所有FileItem的list集合。
3. setFileSizeMax(long fileSizeMax),设置上传文件的最大尺寸值。
4. setSizeMax(long sizeMax),设置上传文件总量的最大值。
5. setHeaderEncoding(java.lang.String encoding),设置编码格式。如果文件路径中存在中文可能会造成文件路径乱码,用此方法处理可以解决。
6. setProgressListener(ProgressListener pListener),设置进程监听器,与AWT和Swing的事件处理机制一样。文件上传一点就会触发ProgressListener,这样我们就可以获取文件上传的进度。
上传文件案例:
public class FileuploadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 创建文件处理工厂,它用于生成FileItem对象。
DiskFileItemFactory difactory = new DiskFileItemFactory();
//设置缓存大小,如果上传文件超过缓存大小,将使用临时目录做为缓存。
difactory.setSizeThreshold(1024 * 1024);
// 设置处理工厂缓存的临时目录,此目录下的文件需要手动删除。
String dir = this.getServletContext().getRealPath("/");
File filedir = new File(dir + "filetemp");
if (!filedir.exists())
filedir.mkdir();
difactory.setRepository(filedir);
// 设置文件实际保存的目录
String userdir = dir + "files";
File fudir = new File(userdir);
if (!fudir.exists())
fudir.mkdir();
// 创建request的解析器,它会将数据封装到FileItem对象中。
ServletFileUpload sfu = new ServletFileUpload(difactory);
// 解析保存在request中的数据并返回list集合
List list = null;
try {
list = sfu.parseRequest(request);
} catch (FileUploadException e) {
e.printStackTrace();
}
// 遍历list集合,取出每一个输入项的FileItem对象,并分别获取数据
for (Iterator it = list.iterator(); it.hasNext();) {
FileItem fi = (FileItem) it.next();
if (fi.isFormField()) {
System.out.println(fi.getFieldName());
System.out.println(fi.getString());
} else {
//由于客户端向服务器发送的文件是客户端的全路径,在这我们只需要文件名即可
String filename = fi.getName();
int index = filename.lastIndexOf("\\");
if(index != -1)
filename = filename.substring(index+1);
//向服务器写出文件
InputStream in = fi.getInputStream();
FileOutputStream fos = new FileOutputStream(fudir + "/" +filename);
byte[] buf = new byte[1024];
int len = -1;
while((len = in.read(buf)) != -1){
fos.write(buf, 0, len);
}
// 关闭流
if(in != null){
try{
in.close();
}finally{
if(fos!=null)
fos.close();
}
}
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
上面的代码只是功能的练习,实际开发中的文件上传需要考虑诸多因素,我们接下来继续学习。
JS动态添加文件上传框和按钮的JavaScript代码:
function add(){
var file = document.createElement("input");
file.type = "file";
file.name = "file";
var butt = document.createElement("input");
butt.type = "button";
butt.value = "删除";
butt.onclick = function rem(){
//必须使用按钮的父节点DIV的父节点来删除自己和自己的父节点DIV。
this.parentNode.parentNode.removeChild(this.parentNode);
};
var div = document.createElement("div");
div.appendChild(file);
div.appendChild(butt);
var parent = document.getElementById("files");
parent.appendChild(div);
}
上传文件的处理细节(1):
1.
中文文件乱码的问题,可以调用两个方法来设置字符编码:servletUpLoader.setHeaderEncoding()或
request.setCharacterEncoding()。我们可以在源文件的创建ServletFileUpload对象后边添加如下代码:
sfu.setHeaderEncoding("UTF-8");
2.
临时文件的删除,如果临时文件大于setSizeThreshold设置的缓存大小,Commons-fileupload组件将使用
setRepository设置的临时目录来保存上传的文件,上传完成后我们需要手动调用FileItem.delete来删除临时文件。建议不要修改缓
存区大小,如果设置缓存为1MB,1000个用户上传文件就需要1000MB内存,服务器会受不了的。我们删除掉setSizeThreshold的代
码,并在每次完成一个文件后添加下而的代码:
// 删除临时目录中的文件
fi.delete();
上传文件的处理细节(2):
1.
在上面的代码中,我们将文件的实际保存目录设置在WEB-INF目录之外。这样外部可以直接访问被上传的文件,这会造成安全问题。比如用户上传了一个带有
恶意脚本功能的JSP文件,然后从外部访问执行了JSP文件…后果不堪设想。所以我们将源代码中对应位置处修改如下:
// 之所以放在"WEB-INF"目录下是为了防止上传的文件被直接被访问的安全问题
String userdir = dir + "WEB-INF/files";
2. 一个WEB应用会许多不同的用户访问,不同的用户可能会上传相同名称的文件,如果这样可能会造成文件覆盖的情况发生,所以我们必须保证文件名称的唯一性,我编写一个方法来生成唯一性名称的文件名:
/**
* 生成具有唯一性的UUID文件名称
* @param fileName
* @return
*/
private String uuidName(String fileName){
UUID uuid = UUID.randomUUID();
return uuid.toString() + "_" + fileName;
}
我们将代码“filename = filename.substring(index + 1);”修改为:filename = uuidName(filename.substring(index + 1));
3. 如果一个目录下的文件过多,会极大减慢文件的访问速度。比如一个目录下的文件如果超过1000个,达到1万个呢?恐怖!我们必须编写一个目录结构生成算法,来分散存上传的文件。我们一个方法:
/**
* 使用哈希算法生成的文件路径
* @param dir
* @param fileName
* @return
*/
private String hashPath(String dir, String fileName) {
int hashCode = fileName.hashCode();
int dir1 = (hashCode >> 4) & 0xf;
int dir2 = hashCode & 0xf;
String newpath = dir + "/" + dir1 + "/" + dir2 + "/";
File file = new File(newpath);
if(!file.exists()){
file.mkdirs();
}
return newpath + uuidName(fileName);
}
上传文件的处理细节(3)
1. 使用ProgressListener显示上传文件进度,在创建ServletFileUpload之后添加如下代码:
// 设置文件上传进度监听器
sfu.setProgressListener(new ProgressListener() {
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("已上传:" + pBytesRead + " 总大小:"
+ pContentLength);
}
});
2. 上面的代码会造成频繁的打印,为了使它在上传一定数量后再打印,比如上传10KB后再打印,我们修改上面的代码如下:
// 设置文件上传进度监听器
sfu.setProgressListener(new ProgressListener() {
long temp = -1;
public void update(long pBytesRead, long pContentLength, int pItems) {
long size = pBytesRead / 1024 * 1024 * 10;
if(temp == size)
return;
temp = size;
if(pBytesRead != -1)
System.out.println("已上传:" + pBytesRead + " 总大小:"
+ pContentLength);
else
System.out.println("上传完成!");
}
});
上面的代码比较经典,好好回味一下。
文件下载:
WEB应用中实现文件下载的两种方式:
1. 超链接直接指向下载资源
2. 程序实现下载需设置两个响应头:
(1). 设置Content-Type 的值为:application/x-msdownload。Web 服务器需要告诉浏览器其所输出的内容的类型不是普通的文本文件或 HTML 文件,而是一个要保存到本地的下载文件。
(2). Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置
Content-Disposition 报头。该报头指定了接收程序处理数据内容的方式,在 HTTP 应用中只有 attachment
是标准方式,attachment 表示要求用户干预。在 attachment 后面还可以指定 filename
参数,该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。在设置 Content-Dispostion 之前一定要指定
Content-Type。
为实现文件下载,首先我们遍历目录下所有文件,Servlet:
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ListFileServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取目录
String dir = this.getServletContext().getRealPath("/WEB-INF/files");
HashMap map = new HashMap();
listFile(new File(dir), map);
// 将文件列表设置到request的属性中,然后由JSP页面打印列表。
request.setAttribute("filemap", map);
request.getRequestDispatcher("/list.jsp").forward(request, response);
}
/**
* 使用递归算法,将所有子目录中的文件添加到列表中
*
* @param f
* @param l
*/
private void listFile(File f, HashMap map) {
if (f.isFile()) {
String path = f.getAbsolutePath().substring(
this.getServletContext().getRealPath("/").length());
String name = f.getName();
name = name.substring(name.indexOf("_")+1);
//BASE64Encoder encoder = new BASE64Encoder();
map.put(path, name);
} else {
File[] files = f.listFiles();
for (int i = 0; i < files.length; i++) {
listFile(files[i], map);
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
[转]java web 文件上传的更多相关文章
- Java Web文件上传
参考资料:http://www.cnblogs.com/xdp-gacl/p/4200090.html 一.问题描述 Java Web文件上传需要借助一些第三方库,常用的是借助Apache的包,有两个 ...
- Java Web文件上传原理分析(不借助开源fileupload上传jar包)
Java Web文件上传原理分析(不借助开源fileupload上传jar包) 博客分类: Java Web 最近在面试IBM时,面试官突然问到:如果让你自己实现一个文件上传,你的代码要如何写,不 ...
- java web 文件上传下载
文件上传下载案例: 首先是此案例工程的目录结构:
- Java web文件上传下载
[版权申明:本文系作者原创,转载请注明出处] 文章出处:http://blog.csdn.net/sdksdk0/article/details/52048666 作者:朱培 ID:sdksdk0 邮 ...
- java Web 文件上传
注意:请求实体过大的问题,请修改Nginx服务器的大小(百度参考413 Request Entity Too Large 的解决方法)jsp:<input type="file&quo ...
- java进行文件上传,带进度条
网上看到别人发过的一个java上传的代码,自己写了个完整的,附带源码 项目环境:jkd7.tomcat7. jar包:commons-fileupload-1.2.1.jar.commons-io-1 ...
- servlet web文件上传
web文件上传也是一种POST方式,特别之处在于,需设置FORM的enctype属性为multipart/form-data. 并且需要使用文件域. servlet的代码比较关键是这几句: // 使用 ...
- java+大文件上传解决方案
众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路. 实现文件夹 ...
- H5+JAVA的文件上传,断点续传
这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数 下面直接贴代码吧,一些难懂的我大部分都加上注释了: 上传文件实体类: 看得 ...
随机推荐
- django 项目创建使用
1. web框架的本质: socket服务端 与 浏览器的通信 2. socket服务端功能划分: a. 负责与浏览器收发消息(socket通信) --> wsgiref/uWsgi/gunic ...
- kali Linux 入门(二)
九.软件安装 1.apt install --软件名称-- -y 2.apt install packge_name----库安装 3.apt install kali-linux-all -y--- ...
- 三、ARM 寄存器及异常处理
3.1 ARM 内部寄存器 ARM920T 总共有 37 个寄存器,其中 31 通用 32 位寄存器和 6 个状态寄存器,但不能在同一时刻对所有的寄存器可见.处理器状态和运行模式决定了哪些寄存器对程序 ...
- python笔记(2)---不定长参数
python自定义函数中有两种不定长参数, 第一种是*name:加了星号 * 的参数会以元组(tuple)的形式导入 第二种是**name:加了星号 * *的参数会以字典(dict)的形式导入 *na ...
- LeetCode--051--N皇后(java)-star
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击. 上图为 8 皇后问题的一种解法. 给定一个整数 n,返回所有不同的 n 皇后问题的解决方案. 每一种解 ...
- 【ElicitSearch】启动流程
一.集群启动流程 1.选举主节点 许多节点启动,集群干的第一件事儿就是选主,之后的的流程由主节点触发. 先确定唯一的.大家公认的主节点:再想办法把最新的及其原数据复制到选举的主节点上. 选主是对Bul ...
- OC 类的load方法
+(void)load 方法 不需要主动调用,类加载时会走这个方法
- 【説明する】DS
其实就是数据结构课后题整理....只会一个是什么鬼 染色问题: 线段树? 功能太强大了! 我们并不需要那么多的功能 运用并查集!!! 将相同的并为一段 BZOJ 2375(讲真我没找到这个题在哪里.. ...
- Oracle数据库一些操作信息
Oracle数据库如何查看当前用户角色权限及默认表空间查看当前用户的一些信息,包括用户拥有的角色权限信息.用户表空间以及用户和默认表空间的关系等--查看用户的角色权限1.查看当前用户拥有的角色权限信息 ...
- sql-hive笔试题整理 1 (学生表-成绩表-课程表-教师表)
题记:一直在写各种sql查询语句,最长的有一百多行,自信什么需求都可以接,可......,想了想,可能一直在固定的场景下写,平时也是以满足实际需求为目的,竟不知道应试的题都是怎么出的,又应该怎么做.遂 ...