java web 文件上传与下载_java web(四)文件上传与下载

本文详细介绍了Java Web环境下文件上传和下载的原理与实现,包括使用SmartUpload组件进行文件上传,处理文件重名,限制上传类型,批量上传,以及FileUpload组件的使用。还展示了使用Servlet处理文件上传的完整流程,并提供了处理文件上传过程中的注意事项和最佳实践。
摘要由CSDN通过智能技术生成

一、文件上传原理

1、在TCP/IP中,最早出现的文件上传机制是FTP ,它是将文件由客户端发送到服务器的标准机制;但是在jsp使用过程中不能使用FTP方法上传文件,这是由jsp运行机制所决定。

通过为表单元素设置 method="post" enctype="multipart/form-data" 属性 ,让表单提交的数据以二进制编码的方式提交,在接受此请求的Servlet中用二进制流来获取内容,就可以取得上传文件的内容,从而实现文件的上传。

2、表单enctype属相

1>application/x-www-form-urlencoded

默认编码方式,只处理表单域里的value属性值,采用这种编码方式的表单域中的值处理成URL编码方式。

2>multipart/form-data

这种编码方式的表单会以二进制流的方式处理表单数据,  这种编码方式会把文件域指定的文件内容也封装到请求参数中。

3>text/plain

这种方式主要适用于直接通过表单发送邮件的方式。

二、SmartUpload上传组件的使用

SmartUpload组件使用简单,可以轻松的实现上传文件类型的限制,也可以轻易地取得上传文件的名称、后缀、大小。  使用该组件需要导入jar包,我用的是 jspsmartupload.jar。

1、上传单个文件

upload.htm

文件上传

请选择文件:

upload.jsp

文件上传2

SmartUpload smart=new SmartUpload();

smart.initialize(pageContext);

smart.upload();

smart.save("upload");

%>

这里要注意在jsp页面引入导入jar包中的类文件:上面是com.jspsmart.upload.*,为什么这样写,可以用360压缩打开jar包文件如下:

0d8b6e976da4218316d9e44ffa69fe3a.png

upload表示上传文件的保存文件夹,该文件夹要在跟目录手动建立。上传结果如下:

ace024e5e9ff1e9de34906494da59833.png

2、混合表单

如果要上传文件,表单必须封装,但是表单使用了enctype 封装后其他非文件类的表单控件无法通过request内置对象取得,此时必须通过SmartUpload类的getRequest()方法取得全部请求参数。

upload2.htm

文件上传

姓名:

照片:

upload2.jsp

文件上传2

SmartUpload smart=new SmartUpload();    //实例化SmartUpload上传组件

smart.initialize(pageContext);          //初始化上传操作

smart.upload();                  //上传准备

String name=smart.getRequest().getParameter("uname");  //接受请求参数

smart.save("upload");              //将上传文件保存在upload文件夹中

%>

姓名:

request=:

f1cd92962a0d0a7af48f87617631d112.png

3、为上传文件自动命名

主要为了防止重名情况下发生覆盖文件。以下例子中文件命名采用 IP地址+时间戳+三位随机数

upload3.htm

文件上传

姓名:

照片:

upload3.jsp

文件上传3

request.setCharacterEncoding("utf-8");

//实例化SmartUpload上传组件

SmartUpload smart=new SmartUpload();

//初始化上传操作

smart.initialize(pageContext);

//上传准备

smart.upload();

//接受请求参数

String name=smart.getRequest().getParameter("uname");

IPTimeStamp its=new IPTimeStamp(request.getRemoteAddr());

//取得文件后缀名

String ext=smart.getFiles().getFile(0).getFileExt();

//拼凑文件名称

String fileName=its.getIPTimeRand()+"."+ext;

System.out.println("文件名称及后缀==>"+fileName);

//保存文件

smart.getFiles().getFile(0).saveAs(getServletContext().getRealPath("/")

+"upload"+java.io.File.separator+fileName);

//./表示当前目录;../表示源文件所在目录的上一级目录;../../表示源文件所在目录的上上级目录,以此类推

%>

姓名:

<%=fileName%>

IPTimeStamp.java

package kk.upload3;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.Random;

public class IPTimeStamp{

private SimpleDateFormat sdf=null;

private String ip=null;

public IPTimeStamp(){

}

public IPTimeStamp(String ip){

this.ip=ip;

//System.out.println("ip="+ip);

}

//得到IP地址+时间戳+三位随机数

public String getIPTimeRand(){

StringBuffer buf=new StringBuffer();

if(this.ip!=null){

/*

* 进行拆分操作

* 在java中 \代表转义字符 \n \t 等,而 \\ 代表一个反斜杠 而.代表一个元字符

* 要表示一个.就需要用 要用\.

* 所以"\\." 在实际编译中 就代表 .

* */

String s[]=this.ip.split("\\.");

for(int i=0;i

//不够三位数字补0

buf.append(this.addZero(s[i],3));

}

}

//取得时间戳

buf.append(this.getTimeStamp());

Random r=new Random();

for(int i=0;i<3;i++){

buf.append(r.nextInt(10));

}

System.out.println("文件前缀==>"+buf.toString());

return buf.toString();

}

private String addZero(String str,int len){

StringBuffer s=new StringBuffer();

s.append(str);

while(s.length()

s.insert(0,"0");

}

//System.out.println(s);

return s.toString();

}

public String getDate(){

this.sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

return this.sdf.format(new Date());

}

public String getTimeStamp(){

this.sdf=new SimpleDateFormat("yyyyMMddHHmmssSSS");

return this.sdf.format(new Date());

}

}

运行结果:

7d5a64acc19763bc2fc90e0dceb3fbb1.png

问题:限制文件上传类型?

比如验证上传文件后缀的合法性:

//表示只允许后缀为jpg/gif的文件上传。

if(smart.getFiles().getFile(0).getFileName().matches("^\\w+\\.(jpg|gif)$")){

}

4、批量上传

对upload3.jsp文件进行如下更改,其他的都不用改变,

文件上传3

request.setCharacterEncoding("utf-8");

SmartUpload smart=new SmartUpload();

smart.initialize(pageContext);

smart.upload();

String name=smart.getRequest().getParameter("uname");

IPTimeStamp its=new IPTimeStamp(request.getRemoteAddr());

for(int x=0;x

String ext=smart.getFiles().getFile(x).getFileExt();

String fileName=its.getIPTimeRand()+"."+ext;

smart.getFiles().getFile(x).saveAs(getServletContext().getRealPath("/")

+"upload"+java.io.File.separator+fileName);

}

%>

获得上传文件数量:smart.getFiles().getCount()。

三、FileUpload上传组件的使用

1、接收上传内容

FileUpload的具体上传操作与SmartUpload相比有着很高的复杂度;FileUpload上传基本步骤如下:

<1>创建磁盘工厂:DiskFileItemFactory factory=new DiskFileItemFactory();

<2>创建处理工具:ServletFileUpload upload=new ServletFileUpload(factory);

<3>设置上传文件大小:upload.setFileSizeMax(3145728);

<4>接收全部内容:List items=upload.parseRequest(request);

由于FileUpload会将所有上传的内容(包括文件和普通参数)一起接收,所以要依次判断每一次上传的内容是文件还是普通文本。在使用FileUpload接收时所有提交的内容都会通过upload.parseRequest()方法返回,然后在使用Iterator依次取出每一个提交内容。

fileUpload.htm

文件上传

姓名:

照片:

fileUpload.jsp

文件上传2

DiskFileItemFactory factory=new DiskFileItemFactory();   //创建磁盘工厂

ServletFileUpload upload=new ServletFileUpload(factory);  //创建处理工具

upload.setFileSizeMax(3145728);   //设置最大上传大小为3MB

upload.setHeaderEncoding("UTF-8");    //解决上传文件名的中文乱码

List items=upload.parseRequest(request);    //接受全部内容

Iterator iter=items.iterator(); //将全部的内容变成Iterator实例

while(iter.hasNext()){

FileItem item=iter.next(); //取出每一个上传的文件

String fieldName=item.getFieldName(); //取得表单控件的名称

%>

表单控件名:-->

if(!item.isFormField()){  //不是普通的文本文件,是上传文件

String fileName=item.getName(); //获取上传文件的文件名

String contentType=item.getContentType();  //获得文件类型

long sizeInBytes=item.getSize(); //获得文件的大小

%>

上传文件名:上传文件类型:上传文件大小:

}else{

String value=item.getString();

%>

普通参数:

}

%>

}

%>

使用FileUpload组件接收完全部的数据后,所有的文件都保存在List集合中,所以需要Iterator来取出每一个数据,但由于其中包含普通的文本数据和上传的文件,每一个上传的内容都是用FileItem类对象表示,所以当时用迭代器取出每一个FileItem对象时就可以使用FileItem类中的isFormField()方法来判断当前操作的内容是普通的文本还是上传的文件,如果是上传的文件则将文件的内容依次取出;如果是普通的文本则直接通过getString()方法取得具体信息; 程序运行结果如下图:

0d4f7972944bcb46cd0044358e352e12.png

a177bf848c1463da893e3bd1d98d60e5.png

问题:乱码,我使用的是utf-8编码,保存jsp文件的时候也是将文件另存为utf-8编码,但是姓名如果输入中文就会显示乱码;

af4be9e9680181ee21ea0891f2cb921d.png

9b1453c9095c8470247daa44e5547aab.png

2、以上所有的文件上传都是htm+jsp完成的,所有的Java代码都是写在jsp文件中,下面将使用jsp+Servlet完成文件上传。

fileUpload2.jsp

pageEncoding="UTF-8"%>

/p>

"http://www.w3.org/TR/html4/loose.dtd">

文件上传

文件上传实例

上传用户:

上传文件1:

上传文件2:

fileMessage2.jsp

pageEncoding="UTF-8"%>

/p>

"http://www.w3.org/TR/html4/loose.dtd">

文件上传结果

${message}

UploadServlet.java文件

package kk.fileUpload;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

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.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class UploadServlet extends HttpServlet {

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

//得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全

String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");

File file = new File(savePath);

//判断上传文件的保存目录是否存在

if (!file.exists() && !file.isDirectory()) {

System.out.println(savePath+"目录不存在,需要创建");

//创建目录

file.mkdir();

}

//消息提示

String message = "";

try {

//使用Apache文件上传组件处理文件上传步骤:

//1、创建一个DiskFileItemFactory工厂

DiskFileItemFactory factory = new DiskFileItemFactory();

//2、创建一个文件上传解析器

ServletFileUpload upload = new ServletFileUpload(factory);

//解决上传文件名的中文乱码

upload.setHeaderEncoding("UTF-8");

//3、判断提交上来的数据是否是上传表单的数据

if(!ServletFileUpload.isMultipartContent(request)){

//按照传统方式获取数据

return;

}

//4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List集合,每一个FileItem对应一个Form表单的输入项

List list = upload.parseRequest(request);

for(FileItem item : list){

//如果fileitem中封装的是普通输入项的数据

if(item.isFormField()){

String name = item.getFieldName();

//解决普通输入项的数据的中文乱码问题

String value = item.getString("UTF-8");

//value = new String(value.getBytes("iso8859-1"),"UTF-8");

System.out.println(name + "=" + value);

}else{

//如果fileitem中封装的是上传文件

//得到上传的文件名称,

String filename = item.getName();

System.out.println(filename);

if(filename==null || filename.trim().equals("")){

continue;

}

//注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt

//处理获取到的上传文件的文件名的路径部分,只保留文件名部分

filename = filename.substring(filename.lastIndexOf("\\")+1);

//获取item中的上传文件的输入流

InputStream in = item.getInputStream();

//创建一个文件输出流

FileOutputStream out = new FileOutputStream(savePath + "\\" + filename);

//创建一个缓冲区

byte buffer[] = new byte[1024];

//判断输入流中的数据是否已经读完的标识

int len = 0;

//循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据

while((len=in.read(buffer))>0){

//使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中

out.write(buffer, 0, len);

}

//关闭输入流

in.close();

//关闭输出流

out.close();

//删除处理文件上传时生成的临时文件

item.delete();

message = "文件上传成功!";

}

}

}catch (Exception e) {

message= "文件上传失败!";

e.printStackTrace();

}

request.setAttribute("message",message);

request.getRequestDispatcher("/fileMessage2.jsp").forward(request, response);

}

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {

doGet(req, resp);

}

}

web.xml

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

version="2.5">

Welcome to Tomcat

Uploadf

kk.fileUpload.UploadServlet

Uploadf

/FileUpload/UploadServlet

问题:从Java Web(一)开始一直使用手工在Tomcat安装目录webapps中创建,没有实用工具,所以在编写上面UploadServlet.java文件的时候提示找不到相应的jar包(commons-fileupload-1.3.2.jar/commons-io-2.5.jar/servlet-api.jar)。

解决办法:

classpath:.;%CATALINA_HOME%/lib/servlet-api.jar;%CATALINA_HOME%/lib/commons-io-2.5.jar;%CATALINA_HOME%/lib/commons-fileupload-1.3.2.jar;.;%JAVA_HOME%/lib/dt.jar;%JAVA_HOME%/lib/tools.jar

经过以上配置成功编译class类文件,然后将class文件放在classes文件夹中(注意按照打包的层级存放)。

运行结果如下:

09efd3f444a6a9c4f20d5500b7d47cf5.png

a865503c1b0fe897af9b60b3778c53b9.png

a606c62a3c2a3651d1761b10a04c9d23.png

21d209da7d99b3a861af812c457370fe.png

四、文件上传小结

上述的代码虽然可以成功将文件上传到服务器上面的指定目录当中,但是文件上传功能有许多需要注意的小细节问题:

1、为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。

2、为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。

3、为防止一个目录下面出现太多文件,要使用hash算法打散存储。

4、要限制上传文件的最大值。

5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。

将java文件按照以上要求更改如下:

package kk.upload3;

import javax.servlet.http.HttpServlet;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.util.List;

import java.util.UUID;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;

import org.apache.commons.fileupload.FileUploadBase;

import org.apache.commons.fileupload.ProgressListener;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class UploadHandleServlet extends HttpServlet{

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

//得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全

String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");

//上传时生成的临时文件保存目录

String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");

File tmpFile = new File(tempPath);

if (!tmpFile.exists()) {

//创建临时目录

tmpFile.mkdir();

}

//消息提示

String message = "";

try{

//使用Apache文件上传组件处理文件上传步骤:

//1、创建一个DiskFileItemFactory工厂

DiskFileItemFactory factory = new DiskFileItemFactory();

/*

* 设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中。

* 设置缓冲区的大小为100KB,如果不指定,那么缓冲区的大小默认是10KB

* */

factory.setSizeThreshold(1024*100);

//设置上传时生成的临时文件的保存目录

factory.setRepository(tmpFile);

//2、创建一个文件上传解析器

ServletFileUpload upload = new ServletFileUpload(factory);

//监听文件上传进度

upload.setProgressListener(new ProgressListener(){

public void update(long pBytesRead, long pContentLength, int arg2) {

System.out.println("文件大小为:" + pContentLength + ",当前已处理:" + pBytesRead);

/**

* 文件大小为:14608,当前已处理:4096

* 文件大小为:14608,当前已处理:7367

* 文件大小为:14608,当前已处理:11419

* 文件大小为:14608,当前已处理:14608

*/

}

});

//解决上传文件名的中文乱码

upload.setHeaderEncoding("UTF-8");

//3、判断提交上来的数据是否是上传表单的数据

if(!ServletFileUpload.isMultipartContent(request)){

//按照传统方式获取数据

return;

}

//设置上传单个文件的大小的最大值,目前是设置为1024*1024字节,也就是1MB

upload.setFileSizeMax(1024*1024);

//设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和,目前设置为10MB

upload.setSizeMax(1024*1024*10);

//4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List集合,每一个FileItem对应一个Form表单的输入项

List list = upload.parseRequest(request);

for(FileItem item : list){

//如果fileitem中封装的是普通输入项的数据

if(item.isFormField()){

String name = item.getFieldName();

//解决普通输入项的数据的中文乱码问题

String value = item.getString("UTF-8");

//value = new String(value.getBytes("iso8859-1"),"UTF-8");

System.out.println(name + "=" + value);

}else{

//如果fileitem中封装的是上传文件

//得到上传的文件名称,

String filename = item.getName();

System.out.println(filename);

if(filename==null || filename.trim().equals("")){

continue;

}

//注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt

//处理获取到的上传文件的文件名的路径部分,只保留文件名部分

filename = filename.substring(filename.lastIndexOf("\\")+1);

//得到上传文件的扩展名

String fileExtName = filename.substring(filename.lastIndexOf(".")+1);

//如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上传的文件类型是否合法

System.out.println("上传的文件的扩展名是:"+fileExtName);

//获取item中的上传文件的输入流

InputStream in = item.getInputStream();

//得到文件保存的名称

String saveFilename = makeFileName(filename);

//得到文件的保存目录

String realSavePath = makePath(saveFilename, savePath);

//创建一个文件输出流

FileOutputStream out = new FileOutputStream(realSavePath + "\\" + saveFilename);

//创建一个缓冲区

byte buffer[] = new byte[1024];

//判断输入流中的数据是否已经读完的标识

int len = 0;

//循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据

while((len=in.read(buffer))>0){

//使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中

out.write(buffer, 0, len);

}

//关闭输入流

in.close();

//关闭输出流

out.close();

//删除处理文件上传时生成的临时文件

//item.delete();

message = "文件上传成功!";

}

}

}catch (FileUploadBase.FileSizeLimitExceededException e) {

e.printStackTrace();

request.setAttribute("message", "单个文件超出最大值!!!");

request.getRequestDispatcher("/message.jsp").forward(request, response);

return;

}catch (FileUploadBase.SizeLimitExceededException e) {

e.printStackTrace();

request.setAttribute("message", "上传文件的总的大小超出限制的最大值!!!");

request.getRequestDispatcher("/message.jsp").forward(request, response);

return;

}catch (Exception e) {

message= "文件上传失败!";

e.printStackTrace();

}

request.setAttribute("message",message);

request.getRequestDispatcher("/message.jsp").forward(request, response);

}

/**

* @Method: makeFileName

* @Description: 生成上传文件的文件名,文件名以:uuid+"_"+文件的原始名称

* @Anthor:kk

* @param filename 文件的原始名称

* @return uuid+"_"+文件的原始名称

*/

private String makeFileName(String filename){ //2.jpg

//为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名

return UUID.randomUUID().toString() + "_" + filename;

}

/**

* 为防止一个目录下面出现太多文件,要使用hash算法打散存储

* @Method: makePath

* @Description:

* @Anthor:kk

*

* @param filename 文件名,要根据文件名生成存储目录

* @param savePath 文件存储路径

* @return 新的存储目录

*/

private String makePath(String filename,String savePath){

//得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址

int hashcode = filename.hashCode();

int dir1 = hashcode&0xf; //0--15

int dir2 = (hashcode&0xf0)>>4; //0-15

//构造新的保存目录

String dir = savePath + "\\" + dir1 + "\\" + dir2; //upload\2\3 upload\3\5

//File既可以代表文件也可以代表目录

File file = new File(dir);

//如果目录不存在

if(!file.exists()){

//创建目录

file.mkdirs();

}

return dir;

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

运行结果如下:

3d90e1c598486455425acf267c07d531.png

050c4e7919a92fed976ff628fb871bf2.png

66c20e32f213a875279526f831ac0b16.png

fb28c6565fc68f8d4b9dbf2140533c7a.png

d4d94138fe7c624ccbe8b0008ca1d9a9.png

6、开发FileUpload组件的专属操作类

经过以上FileUpload组件的基本操作学习,可以发现仍有诸多不便;

1>无法像使用request.getParameter()方法那样准确地取得提交的参数;

2>无法像使用request.getParameterValues()方法那样准确地取得一组提交的参数;

3>所有的上传文件都要依次判断,才能分别保存,不能一次性批量保存;

如要解决以上问题则需要自己进行代码的扩充,编写一个FileUpload操作的工具类----FileUploadTools(会用即可)。

五、文件下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值