实现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 文件上传的更多相关文章

  1. Java Web文件上传

    参考资料:http://www.cnblogs.com/xdp-gacl/p/4200090.html 一.问题描述 Java Web文件上传需要借助一些第三方库,常用的是借助Apache的包,有两个 ...

  2. Java Web文件上传原理分析(不借助开源fileupload上传jar包)

    Java Web文件上传原理分析(不借助开源fileupload上传jar包) 博客分类: Java Web   最近在面试IBM时,面试官突然问到:如果让你自己实现一个文件上传,你的代码要如何写,不 ...

  3. java web 文件上传下载

    文件上传下载案例: 首先是此案例工程的目录结构:

  4. Java web文件上传下载

    [版权申明:本文系作者原创,转载请注明出处] 文章出处:http://blog.csdn.net/sdksdk0/article/details/52048666 作者:朱培 ID:sdksdk0 邮 ...

  5. java Web 文件上传

    注意:请求实体过大的问题,请修改Nginx服务器的大小(百度参考413 Request Entity Too Large 的解决方法)jsp:<input type="file&quo ...

  6. java进行文件上传,带进度条

    网上看到别人发过的一个java上传的代码,自己写了个完整的,附带源码 项目环境:jkd7.tomcat7. jar包:commons-fileupload-1.2.1.jar.commons-io-1 ...

  7. servlet web文件上传

    web文件上传也是一种POST方式,特别之处在于,需设置FORM的enctype属性为multipart/form-data. 并且需要使用文件域. servlet的代码比较关键是这几句: // 使用 ...

  8. java+大文件上传解决方案

    众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路. 实现文件夹 ...

  9. H5+JAVA的文件上传,断点续传

    这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数 下面直接贴代码吧,一些难懂的我大部分都加上注释了: 上传文件实体类: 看得 ...

随机推荐

  1. ulimit 管理系统资源

    具体的 options 含义以及简单示例可以参考以下表格. 选项 含义 例子 -H 设置硬资源限制,一旦设置不能增加. ulimit – Hs 64:限制硬资源,线程栈大小为 64K. -S 设置软资 ...

  2. JS 控制子页面刷新父页面

    iframe里面的子页,用parent.location.href = parent.location.reload();如果是window.open 打开就用opener.location.relo ...

  3. 2-基于6U VPX的双TMS320C6678+Xilinx FPGA K7 XC7K420T的图像信号处理板

    基于6U VPX的双TMS320C6678+Xilinx FPGA K7 XC7K420T的图像信号处理板 综合图像处理硬件平台包括图像信号处理板2块,视频处理板1块,主控板1块,电源板1块,VPX背 ...

  4. Linux性能优化从入门到实战:17 网络篇:网络基础

    网络模型 为了解决网络互联中异构设备的兼容性问题,并解耦复杂的网络包处理流程,国际标准化组织制定了开放式系统互联通信参考模型(Open System Interconnection Reference ...

  5. [Tyvj1423]GF和猫咪的玩具(最短路)

    [Tyvj1423]GF和猫咪的玩具 题目描述 GF同学和猫咪得到了一个特别的玩具,这个玩具由n个金属环(编号为1---n),和m条绳索组成,每条绳索连接两个不同的金属环,并且长度相同.GF左手拿起金 ...

  6. 阿里云轻应用云服务器配置tomcat

    #etc/profile export CATALINA_HOME=/wocloud/tomcat_cluster/tomcat1/apache-tomcat-7.0.57 #查看防火墙状态 fire ...

  7. 安装phpredis扩展以及phpRedisAdmin工具

    先从phpredis的git拿到最新的源码包:wget https://github.com/nicolasff/phpredis/archive/master.tar.gz 然后解压到进入目录:ta ...

  8. vue开发可复用组件

    组件,是一个具有一定功能,且不同组件间功能相对独立的模块.高内聚.低耦合.   开发可复用性的组件应遵循以下原则:   1.规范化命名:组件的命名应该跟业务无关,而是依据组件的功能命名. 2.数据扁平 ...

  9. 小记:web安全测试之——固定session漏洞

    今天因为项目背景需要,需要检测web接口是否一些安全隐患. 无奈于从未掌握有系统的渗透性知识,只好根据个人对网络协议和 web 的理解,做一些探索,最终发现了一个session fixation at ...

  10. 对Promise的研究2

    3.Promise.prototype.then() Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的.它的作用是为 Promise ...