JAVA EE-文件上传
应该相信,自己是生活的战胜者。 —— 雨果
文件下载
首先我们复习一下文件下载的内容
静态下载
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1 准备工作
//设置content-type
response.setContentType(getServletContext().getMimeType("xxx.zip"));
//添加响应头 content-disposition
response.setHeader("Content-Disposition", "attachment;filename=apache-tomcat-6.0.35.zip");
//2 获得输入流
InputStream is
= getServletContext().getResourceAsStream("/WEB-INF/apache-tomcat-6.0.35.zip");
//3 获得输出流
OutputStream os = response.getOutputStream();
//4 两个流对接
IOUtils.copy(is, os);
}
}
根据用户的选择来获取下载
JSP文件:
<body>
<a href="/day12-download/AServlet" >点击下载</a><br>
<a href="/day12-download/BServlet?name=apache-tomcat-6.0.35.zip" >apache-tomcat-6.0.35.zip</a><br>
<a href="/day12-download/BServlet?name=激活软件_v10.0.0.exe" >激活软件_v10.0.0.exe</a><br>
</body>
业务处理的Servlet
public class BServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1 获得参数,拿到用户需要下载的文件名称
String fileName = request.getParameter("name");
fileName = new String(fileName.getBytes("ISO-8859-1"),"UTF-8");
//2 准备工作
//设置content-type
response.setContentType(getServletContext().getMimeType(fileName));
//添加响应头 content-disposition
response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8"));
//2 获得输入流
InputStream is
= getServletContext().getResourceAsStream("/WEB-INF/"+fileName);
//3 获得输出流
OutputStream os = response.getOutputStream();
//4 两个流对接
IOUtils.copy(is, os);
//关闭输出流
is.close();
os.close();
}
}
下载的乱码问题
客户端的超连接中如果包含了中文,会出现get请求方式乱码问题
<a href="${pageContext.request.contextPath}/download?filename=天空.mp3">下载王非天空.mp3</a>
String filename = request.getParameter("name"); // yog.rar / 她说.mp3
byte[] b = filename.getBytes("ISO-8859-1");
filename = new String(b,"UTF-8");
实际上Get提交时包含中文乱码的解决。
如果下载文件名称中出现了中文乱码问题
<a href="${pageContext.request.contextPath}/download?filename=天空.mp3">下载王非天空.mp3</a>
String filename = request.getParameter("name"); // yog.rar / 她说.mp3
byte[] b = filename.getBytes("ISO-8859-1");
filename = new String(b,"UTF-8");
实际上Get提交时包含中文乱码的解决。
文件上传
为什么使用文件上传?
- 例如社交网站需要提供用户照片.
- 很多企业内部,商品网站维护时需要上传一些文件
- 邮箱需要上传附件….
文件上传的操作
- 请求方式必须是post
- 需要使用组件
- 表单必须设置encType=”multipart/form-data”
- 上传文件的input 中,name属性必须要填写,不然不会上传.
这样一来,通过request对象,获取InputStream,可以将浏览器提交的所有请求正文读取到.
文件上传需要的插件
commons-fileupload 它是常用的一个文件上传工具
使用时要导入两个jar包
commons-fileupload-1.2.1.jar
commons-io-1.4.jar
一个上传的浏览器端代码
<body>
<!--
1.文件上传,表单必须是post提交
2.文件上传,还需要在表单上加上enctype属性(提交请求正文的类型)
*application/x-www-form-urlencoded(默认取值,普通提交)
*multipart/form-data(多段式提交)
3. 文件上传,使用<input type="file" name="photo" /> 标签,并且必须有name属性
-->
<form action="/day12-upload/BServlet" method="post" encType="multipart/form-data" >
用户名:<input type="text" name="name" /><br>
个人近照:<input type="file" name="photo" /><br>
<input type="submit" value="上传" />
</form>
</body>
下面演示如何使用第三方的jar包来优化文件上传处理
public class BServlet extends HttpServlet {
//使用文件上传工具类(第三方)
// 导包 => commons-fileupload-1.2.1.jar
// => commons-io-1.4.jar
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1 创建配置工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//2 根据配置工厂创建解析请求中文件上传内容的解析器
ServletFileUpload upload = new ServletFileUpload(factory);
//3 判断当前请求是否是多段式提交
if(!upload.isMultipartContent(request)){
//普通表单提交
throw new RuntimeException("不是多段式提交!");
}
try {
//4 解析request对象
List<FileItem> list = upload.parseRequest(request);
if(list!=null){
for(FileItem item : list){
//判断当前上传段是普通字段,还是文件上传
if(!item.isFormField()){//文件上传段
String fName = item.getFieldName();
InputStream is = item.getInputStream();//获得文件上传段中,文件的流
FileOutputStream fos = new FileOutputStream("d://upload/xxx.jpg");
IOUtils.copy(is, fos);
fos.close();
}else{//普通字段段
String name = item.getFieldName();
String value = item.getString();
System.out.println(name+"==>"+value);
}
}
}
} catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
文件上传段与普通表单段的区别如下
fileupload工具类详解
public class CServlet extends HttpServlet {
//fileupload 工具类使用详解
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1 创建配置工厂=> 有参的构造可以 直接设置下面两个配置
DiskFileItemFactory factory = new DiskFileItemFactory();
//1.1 设置文件上传临时目录 => 默认位置 => tomcat/temp
factory.setRepository(new File("d:/temp"));
//1.2 设置文件写入硬盘的缓冲区大小=>默认值=>10k
factory.setSizeThreshold(10240);
//2 根据配置工厂创建解析器(解析request对象)
ServletFileUpload upload = new ServletFileUpload(factory);
//2.1 判断当前请求是否是多段式提交
upload.isMultipartContent(request);
//2.2 设置多段中每段 段头 在解析时,使用什么码表解码 => 当段头中出现中文时,一定要调用该方式指定段头码表
upload.setHeaderEncoding("UTF-8");
//2.3 设置文件最大上传大小 (单位:字节)
upload.setSizeMax(1024*1024*10); //单次请求,总上传大小限制 10兆
upload.setFileSizeMax(1024*1024);// 每个文件上传段,大小限制 1兆
List<FileItem> list = null;
try {
//2.4 解析request,将每个分段中的内容封装到FileItem中
list = upload.parseRequest(request);
} catch (FileUploadException e) {
e.printStackTrace();
}
if(list!=null){
for(FileItem item : list){
//3.1 item 判断当前分段是否是普通表单段
boolean flag = item.isFormField();
//普通段
//3.2获得 表单提交的键.(input元素,name属性的值)
String fname = item.getFieldName();
//3.3 返回文件名称
String name = item.getName();
//3.4 获得文件上传中的正文
//item.getInputStream();
//3.5 以字符串形式返回段体中的内容 注意:文件上传段不建议使用该方法.
String content= item.getString();
//3.6 删除上传的临时文件
item.delete();
System.out.println("是否是普通表单提交:"+flag+",表单提交的键:"+fname+",文件名称:"+name+",文件内容:"+content);
}
}
}
}
关于乱码问题的研究
当上传的文件名中有中文时,如果码表不当就会造成乱码问题的产生。
public class DServlet extends HttpServlet {
//上传文件名称是中文,乱码问题
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1 创建配置工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//2 根据配置工厂创建解析请求中文件上传内容的解析器
ServletFileUpload upload = new ServletFileUpload(factory);
//解决文件名称中文乱码
upload.setHeaderEncoding("UTF-8");
//3 判断当前请求是否是多段式提交
if(!upload.isMultipartContent(request)){
//普通表单提交
throw new RuntimeException("不是多段式提交!");
}
try {
//4 解析request对象
List<FileItem> list = upload.parseRequest(request);
if(list!=null){
for(FileItem item : list){
//判断当前上传段是普通字段,还是文件上传
if(!item.isFormField()){//文件上传段
String fileName = item.getName();
System.out.println(fileName);
}
}
}
} catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
设置上传文件的最大大小
public class FServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1 创建配置工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//2 根据配置工厂创建解析请求中文件上传内容的解析器
ServletFileUpload upload = new ServletFileUpload(factory);
//设置文件上传大小限制
upload.setFileSizeMax(1024);
try {
upload.parseRequest(request);
} catch (FileUploadException e) {
e.printStackTrace();
throw new RuntimeException("文件大小不能超过1kb!");
}
}
}
可以设置上传文件大小,如果超过设置大小.将会抛出异常(默认没有最大值)
void setFileSizeMax(long fileSizeMax) 设置单个文件上传大小
void setSizeMax(long sizeMax) 设置总文件上传大小
如何实现保存的文件名不会重复
public class GServlet extends HttpServlet {
//如果将文件全部保存 => 保证文件名称不能重复
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1 创建配置工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//2 根据配置工厂创建解析请求中文件上传内容的解析器
ServletFileUpload upload = new ServletFileUpload(factory);
//3 判断当前请求是否是多段式提交
if(!upload.isMultipartContent(request)){
//普通表单提交
throw new RuntimeException("不是多段式提交!");
}
try {
//4 解析request对象
List<FileItem> list = upload.parseRequest(request);
if(list!=null){
for(FileItem item : list){
//判断当前上传段是普通字段,还是文件上传
if(!item.isFormField()){//文件上传段
String fName = item.getFieldName();
InputStream is = item.getInputStream();//获得文件上传段中,文件的流
//保存之前,生成文件名称( 保证永远不重复)
//生成随机字符串即可
String fileName = UUID.randomUUID().toString();
FileOutputStream fos = new FileOutputStream("d:/upload/"+fileName);
IOUtils.copy(is, fos);
fos.close();
}
}
}
} catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
如何防止同一个目录之下上传文件数量过多?
public class HServlet extends HttpServlet {
//如何将上传文件分目录保存
// 使用登录用户的用户名 来创建不同的文件夹. 每个用户上传的文件就放到自己的文件夹中
// 按照日期分目录=> d:/upload/2015/08/24/
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1 创建配置工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//2 根据配置工厂创建解析请求中文件上传内容的解析器
ServletFileUpload upload = new ServletFileUpload(factory);
//3 判断当前请求是否是多段式提交
if(!upload.isMultipartContent(request)){
//普通表单提交
throw new RuntimeException("不是多段式提交!");
}
try {
//4 解析request对象
List<FileItem> list = upload.parseRequest(request);
if(list!=null){
for(FileItem item : list){
//判断当前上传段是普通字段,还是文件上传
if(!item.isFormField()){//文件上传段
String fName = item.getFieldName();
InputStream is = item.getInputStream();//获得文件上传段中,文件的流
// 生成日期目录
//1 获得当前日期=>目录结构 的 路径
SimpleDateFormat format = new SimpleDateFormat("/yyyy/MM/dd/");
String datepath = format.format( new Date()); // /2015/08/24/
//拼接完整保存目录路径
String wholePath = "d:/upload"+datepath;// d:/upload/2015/08/24/
//确保文件夹存在
File directory = new File(wholePath);
if(!directory.exists()){//如果文件夹结构不存在
directory.mkdirs(); //创建文件夹结构
}
//保存之前,生成文件名称( 保证永远不重复)
//生成随机字符串即可
String fileName = UUID.randomUUID().toString();
FileOutputStream fos = new FileOutputStream(wholePath+fileName);
IOUtils.copy(is, fos);
fos.close();
}
}
}
} catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}