基本的文件上传:
最基本的文件上传是在不利用其它额外jar包的帮助下,实现上传,这就是其它jar包封装的基础。
我们在文件上传的时候大多都是利用表单里面的input标签,input标签里面有个type为file,就是定义了一个上传文件的输入框,现在我们要了解的是form表单的enctype属性,这个属性决定的是表单发到服务器之前如何对数据进行编码。
enctype属性有三个值可取:
1.application/x-www-form-urlencoded,是表单该属性的默认值,意思是发到服务器之前对所有的数据进行编码。
2.multipart/form-data,意思是不对字符进行编码,如果你要上传文件,必须用这个值。
3.text/plain,意思空格转换为 "+" 加号,但不对特殊字符编码。
下面我们写个简单的html文件,定义一个表单,用input标签进行文件上传:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>file.html</title> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="this is my page"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> </head> <body> <form action="dealFile.jsp" enctype="multipart/form-data" method="post"> 上传文件:<input type="file" name="file" /><br> 姓名:<input type="text" name="name" /><br> <input type="submit" value="提交"/> </form> </body> </html>
我们都知道,表单的数据是通过http协议传给服务器的,所以服务器的处理也是从http协议中获取数据,对其进行解析,request对象中有一个getInputStream方法,这个方法可以得到一个输入流,从这个流中我们就可以读取到文件的内容,当然也包括其他表单附带的数据:
dealFile.jsp:
<%@ page language="java" import="java.util.*, java.io.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'dealFile.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> <% request.setCharacterEncoding("UTF-8"); InputStream is = request.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8")); String buffer = null; out.println(buffer == null); while((buffer = br.readLine()) != null) { out.println(buffer + "<br>"); } %> </body> </html>
下面介绍使用Common-FileUpload框架进行上传,首先你必须引进两个jar包,一个是Commons-FileUpload.jar和Commons-io.jar,这两个jar包百度都可以搜索到:
我们使用的还是上面的file.html,把它的action改为dealFileByFU.jsp:
dealFileByFU.jsp:
<%@page import="org.apache.commons.fileupload.FileItem"%> <%@page import="org.apache.commons.fileupload.servlet.ServletFileUpload"%> <%@page import="org.apache.commons.fileupload.disk.DiskFileItemFactory"%> <%@ page language="java" import="java.util.*, java.io.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'dealFile.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <% //定义上传工厂 DiskFileItemFactory factory = new DiskFileItemFactory(); //设置工厂的大小限制 factory.setSizeThreshold(1024*1024*20); //设置文件的存放路径 factory.setRepository(new File(request.getRealPath("/"))); //根据工厂创建一个上传文件的ServletFileUpload对象 ServletFileUpload upload = new ServletFileUpload(factory); //设置最大的上传限制 upload.setSizeMax(1024*1024*20); //根绝request对其进行解析,items是所有的表单项 List items = upload.parseRequest(request); //遍历所有的表单项 for(Iterator it = items.iterator(); it.hasNext();) { FileItem item = (FileItem)it.next(); //判断是普通的表单域还是上传文件域 if(item.isFormField()) { //拿到对应的字段名和值 String name = item.getFieldName(); String value = item.getString("UTF-8"); out.print(name + " = " + value); } else { //拿到字段名 String fieldName = item.getFieldName(); //拿到文件名 String fileName = item.getName(); //拿到文件类型 String contentType = item.getContentType(); //命名文件名 FileOutputStream fos = new FileOutputStream(request.getRealPath("/") + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf("."), fileName.length())); //判断内存中是否有该文件 if(item.isInMemory()) { fos.write(item.get()); } else { InputStream is = item.getInputStream(); byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) > 0) { fos.write(buffer, 0, len); } is.close(); fos.close(); } } } %> </body> </html>
接下来介绍的是另外一个小框架COS进行上传,首先我们同样要引入cos.jar包,COS的核心类是MultipartParser,该类用于解析HttpServletRequest请求,取出所有的表单域,每个表单域对应一个Part示例,通过这个Part我们就可以判断这个域是普通的表单域还是文件域。
dealFileByCOS.jsp:
<%@page import="com.oreilly.servlet.multipart.FilePart"%> <%@page import="com.oreilly.servlet.multipart.ParamPart"%> <%@page import="com.oreilly.servlet.multipart.Part"%> <%@page import="com.oreilly.servlet.multipart.MultipartParser"%> <%@ page language="java" import="java.util.*, java.io.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'dealFile.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <% //使用MultipartParser解析request并且设置最大容量为10M MultipartParser mp = new MultipartParser(request, 10*1024*1024); //一个part就代表着一个表单域 Part part; while((part = mp.readNextPart()) != null) { //取得所有表单域的name的属性值 String name = part.getName();] //判断是普通的表单域还是文件域 if(part.isParam()) { //强制转化为普通的表单域 ParamPart paramPart = (ParamPart)part; String value = paramPart.getStringValue("UTF-8"); out.println("Parm: " + name + " = " + value); //String value = paramPart. } else if(part.isFile()) { //强制转化为普通的文件域 FilePart filePart = (FilePart)part; String fileName = filePart.getFileName(); if(fileName != null) { //写进文件中 FileOutputStream fos = new FileOutputStream(request.getRealPath("/") + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf("."), fileName.length())); InputStream is = filePart.getInputStream(); byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) > 0) { fos.write(buffer, 0, len); } is.close(); fos.close(); } } else { out.println("file : name " + name); } out.flush(); } %> </body> </html>
下面介绍Struts2的文件上传,使用Struts框架进行解析,更加的方便,因为里面已经为我们封装好了实现,但是我们千万不要以为Struts里面的全部上传文件都是自己实现的,它里面也是使用小框架实现的,只是它做了更近一步的封装。
在Struts2的default.properties配置文件中,我们可以看到:
所以我们可以发现你导入struts2的jar包的时候是需要导入commons-io.jar和common-fileupload.jar的,struts2默认是使用FileUpload框架。
下面我们写一个action对应我们的表单域:
package com.xujianguo.action; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("all") public class FileUpload extends ActionSupport { private String name; private File upload; private String uploadContentType; private String uploadFileName; private String savePath; public String execute() throws Exception { FileOutputStream fos = new FileOutputStream(getSavePath() + "\" + getUploadFileName()); FileInputStream fis = new FileInputStream(getUpload()); byte[] buffer = new byte[1024]; int len = 0; while((len = fis.read(buffer)) > 0) { fos.write(buffer, 0, len); } return SUCCESS; } public void setSavePath(String value) { this.savePath = value; } public String getSavePath() { return ServletActionContext.getRequest().getRealPath(savePath); } public String getName() { return name; } public void setName(String name) { this.name= name; } public File getUpload() { return upload; } public void setUpload(File upload) { this.upload = upload; } public String getUploadContentType() { return uploadContentType; } public void setUploadContentType(String uploadContentType) { this.uploadContentType = uploadContentType; } public String getUploadFileName() { return uploadFileName; } public void setUploadFileName(String uploadFileName) { this.uploadFileName = uploadFileName; } }
这里必须提醒的是:File类型的变量是对应的表单中input类型为file的name属性的值,其他属性如uploadFileName和uploadContentType都是以它为基础的。
struts.xml中的action配置:
<action name="upload" class="com.xujianguo.action.FileUpload"> <param name="savePath">/upload</param> <result name="success">/showFile.jsp</result> </action>
showFile.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> 上传成功<br> 姓名:<s:property value="name"/><br> 文件为:<img src="<s:property value="'upload/' + uploadFileName" />"/> </body> </html>
使用拦截器实现文件过滤:
Struts2提供了一个文件上传的拦截器-fileUpload,为了让该拦截器起作用,只需要要action中配置一下就ok了,它有两个参数:
allowedTypes:该参数指定允许上传文件的类型
maximumSize:该参数指定上传文件的大小,单位为字节。
struts.xml配置:
<action name="upload" class="com.xujianguo.action.FileUpload"> <interceptor-ref name="fileUpload"> <param name="allowedTypes">image/bmp,image/png,image/gif,image/jpeg</param> <param name="maximumSize">200000</param> </interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> <param name="savePath">/upload</param> <result name="success">/showFile.jsp</result> <result name="input">/file.html</result> </action>
Struts2多文件上传:
其实多文件上传跟单文件上传是同样的道理,只是你的Action方面就不能单用一个File类型去接受多个文件了,我们可以改成List<File>对应去存储文件,修改后的文件为:
package com.xujianguo.action; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.util.List; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("all") public class MultiFileUpload extends ActionSupport { private String name; private List<File> upload; private List<String> uploadContentType; private List<String> uploadFileName; private String savePath; public String execute() throws Exception { List<File> files = getUpload(); for(int i = 0; i < files.size(); i++) { FileOutputStream fos = new FileOutputStream(getSavePath() + "\" + getUploadFileName().get(i)); FileInputStream fis = new FileInputStream(files.get(i)); byte[] buffer = new byte[1024]; int len = 0; while((len = fis.read(buffer)) > 0) { fos.write(buffer, 0, len); } } return SUCCESS; } public void setSavePath(String value) { this.savePath = value; } public String getSavePath() { return ServletActionContext.getRequest().getRealPath(savePath); } public String getName() { return name; } public void setName(String name) { this.name= name; } public List<File> getUpload() { return upload; } public void setUpload(List<File> upload) { this.upload = upload; } public List<String> getUploadContentType() { return uploadContentType; } public void setUploadContentType(List<String> uploadContentType) { this.uploadContentType = uploadContentType; } public List<String> getUploadFileName() { return uploadFileName; } public void setUploadFileName(List<String> uploadFileName) { this.uploadFileName = uploadFileName; } }
其他方面跟单文件的一致,哈哈,是不是很简单呢,不要给多文件吓到了。
Struts2的文件下载:
文件下载也是很简单的,我们简单的演示一下,首先是前端的页面:
<a href="/Struts2Demo/guo/download.action">图片下载</a>
前端最核心的就是这一句,访问action,让Struts去处理诸如文件名为中文之类的麻烦问题,接下来就是要配制我们的struts.xml:
<action name="download" class="com.xujianguo.action.DownloadFile"> <result name="success" type="stream"> <param name="contentType">image/png</param> <param name="contentDisposition">attachment;filename="Snap1.png"</param> <param name="inputName">input</param> </result> </action>
我们一一来看里面的属性:
stream:如果你的要实现文件下载,就必须使用type为stream的result,就是使用一个流。
contentType:指定你下载文件的类型,我这里是image,如果你要的是文本的话就使用text/plain。
contentDisposition:这里指定的是文件名和文件的下载方式,我使用的附件的形式,也就是attachment。
inputName:指定你对应action里面的类型为InputStream的属性,指定的文件的流的获取。
DownloadFile.java:
package com.xujianguo.action; import java.io.InputStream; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") public class DownloadFile extends ActionSupport { public InputStream input; public InputStream getInput() { return ServletActionContext.getServletContext().getResourceAsStream("/upload/Snap1.png"); } public void setInput(InputStream input) { this.input = input; } public String execute() { return SUCCESS; } }