一: 文件的上传:
要求:
1:jsp的页面:
a: form 表单:
b: post 提交:
c: form指定一个属性: enctype="multipart/form-data"
d: file组件:
2: Servlet:
request.getParamter("name");//不生效了:
ServletInputStream in = request.getInputStream();
获得数据, 被封装了in 当中, 理论上可以解析数据。
3: request 请求体当中:
post请求体:
请求首行:
请求头信息:
请求体:
-------------------
普通组件: 只有一个头: Content-Dispositon: name="xxx"
请求体: mrzhang : 填写的内容。
-------------------
文件上传字段: 两个头:
Content-Disposition: form-data; name="file"; filename="C:\Users\Mrzhang\Desktop\copy2.txt"
Content-Type: text/plain MIME
文件上传的内容:
xxxx
-----------
二: 解析request当中的请求体:
1:jar: Apache组织提供的一个公共组件:
commons-fileupload
commons-io
和文件上传相关的两个组件, 这两个组件是强依赖。
2:原理:
把request请求体当中的每一个组件的内容都封装到了一个对象当中:
FileItem对象: 使用对象的方法和属性来获得数据。
3:获得FileItem:
a: 获得工厂:
b: 获得解析器:
c: 使用解析器解析request,得到fileItme 对象:
对象的代码:
// 获得工厂:
DiskFileItemFactory Factory = new DiskFileItemFactory();
//获得解析器:
ServletFileUpload upload = new ServletFileUpload(Factory);
// 解析request,获得所有的FileItem对象:
List<FileItem> list= upload.parseRequest(request);
4: FileItem 类的详解:
isFormField() 判断组件是一个普通组件还是一个文件上传组件: true,普通组件:false: 文件上传组件:
getFieldName() 返回普通字段属性的值。 name= username 获得是username的值:
getString(); 获得普通字段上传的内容。
文件上传组件:
getName();获得文件的名称:
getSize();会的文件上传的字节大小。 long
getInputStream();获得文件上传对应的流:
write(File file) ; 文件的内容写到指定的位置:
三: 文件上传的具体代码;
(1)引入jar包:
(2)准备页面:
(3)准备Servlet:
四: 细节:
1:上传的普通字段的内容: 乱码: getString("utf-8");
2:上传的文件不能被外界直接访问, 应该隐藏起来: 目的,安全;
应该吧文件上传到web-inf下,内容不能被外界直接访问。
获得为web-inf/files的位置: 在tomcat上真实的位置:
ServletContext.getRealPath("/WEB-INF/files");
3: 文件的的文件的路径问题:
不同的浏览器上传的路径不一致。 有的浏览器上传的上传相对路径, 有的浏览器上传的是绝对路径。 大部分浏览器上传的相对路径。
a.txt
c:\user\mrzhang\files\a.txt
截取出文件的名称:
String filename = f2.getName();
//截取:
int index = filename.lastIndexOf("\\");
if(index!=-1){
filename = filename.subString(index+1);
}
4: 上传文件的名称乱码问题:
request.setCharacterEncoding("utf-8");
解析器当中提供了一个方法:
servletFileUpload.setHeaderEncoding("utf-8");// 解析器提供。优先级高: 对 request.setCharacterEncoding("utf-8");的封装
5: 文件同名的问题:
解决方式一: uuid+"filename".后缀名称;
解决方式二: new Date().getTime()+"filename";
6: 上传的文件不能存在同一个目录下 : 上传的目录打散:
a: 时间打散: 获得当前的时间, 创造目录:
b: 文件名称首字母打散: abc.txt : charAt("0")
c: 哈希打散:
实现步骤:
(1) 获得文件名称:
(2)获得哈希值: hashCode(); int
(3)转换成十六进制: Integer.toHexString(); //字符串: 9A8B7C
(4) 获得字符串的前两个: 第一个字母做第一层目录。 第二个字母做第二层目录。 16*16
package com.yidongxueyuan.web.servlet;
import java.io.File;
import java.io.IOException;
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.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/*
* 处理文件上传的servlet:
*
* 1:细节:
* 乱码解决了文件名称上传的乱码,
* 普通字段: getString("utf-8");
*
* 2:对相对路径进行了处理: subString();
*
* 3: 文件名称同名的问题,处理:
*
* 4: 哈希打散:
* filename.hashCode();
* filename.toHexString();
* filename.charAt(0);
*
* File dir= new File(root, charAt(0)+"\"+charAt(1));
*
* File destFile = new File(dir, savefilename);
*
* write();
*
*
*
*
*/
public class FileUploadServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//解决上传文件名称的乱码问题:
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");//告知客户端浏览器响应的内容: text/html charset=utf-8
/*
* 三步走:
* 1:获得工厂:
* 2: 获得解析器:
* 3:解析request:
*/
//获得获得解析器工厂;
DiskFileItemFactory factory = new DiskFileItemFactory(1024*20,new File("C:/Users/Mrzhang/javaEE/javaEE-07/temp"));
// DiskFileItemFactory factory = new DiskFileItemFactory();
//获得解析器:
ServletFileUpload upload = new ServletFileUpload(factory);
//解析之前设置上传单个文件的大小:
// upload.setFileSizeMax(1024*10);//这是最大是10K:
// upload.setSizeMax(1024*1024*10); //设置了整个request 的大小, 如果上传的request 大于设定的最大值, 也会触发异常
//解析request: 获List<FileItem>
try {
List<FileItem> fileItem = upload.parseRequest(request);
//解析fileItem当中的内容: web-inf下的: 目录安全: 不能被外界随机的访问:
FileItem f2 = fileItem.get(1);//文件上传的组件:
/*
* 获得文件的名称:
* f2.getName();
* f2.getInputStream(); 文件当中的内容:
* new FileOutputStream("path");
* 实现流对接:
*/
/*
* 获得要保存的路径:
* WEB-INF/files/
* 目的: 安全: 不能被外界直接通过浏览器访问:
* 获得ServletContext对象: 获得真实路径:
*/
String root = this.getServletContext().getRealPath("/WEB-INF/files/");
//获得上传文件的名称:
String filename = f2.getName();
/*
* 细节的处理:
* 1: 绝对路径的问题: 兼顾小部分浏览器:
*/
int index = filename.lastIndexOf("\\");
if(index != -1){//说明路径当中存在\\ :
filename = filename.substring(index+1);
}
/*
* 处理文件同名的问题: uuid
*/
String savename = CommonsUtils.uuid()+"_"+filename;
/*
* 哈希码 生产多个目录: 将上传的文件存放在多个目录当中:
*/
// 1:获得文件名称的哈希值:
int code = filename.hashCode();// int
// 2:获得code 的十六进制的值:
String hex = Integer.toHexString(code);// 9c0a1d
// 获得前两位: 充当第一层目录 和第二层目录:
char firstDir = hex.charAt(0);
char secDir = hex.charAt(1);
/*
* 获得保存的文件路径:
*/
File dirFile = new File(root, firstDir+"/"+secDir);//存放的目录:
//dirFile 如果不存在,创建:
if(!dirFile.exists()){//说明不存在
//创建“:
dirFile.mkdirs();//创建多层的目录:
}
//创建保存的文件:
File destFile = new File(dirFile, savename);
//保存:
try {
f2.write(destFile);
} catch (Exception e) {
e.printStackTrace();
}
} catch (FileUploadException e) {
// 触发异常: 说明文件过大:
request.setAttribute("msg", "您传的文件过大");
request.getRequestDispatcher("/index.jsp").forward(request, response);
e.printStackTrace();
}
}
}
文件的下载
文件的下载:
原来: 服务器响应回来的数据:text/html 浏览器能够自动解析:
原理: 现在要求服务器传递过来的数据是一个字节数组: 只需要在服务器端响应回来一个字节流即可。
下载的要满足的条件一个流两个头:
流: 服务器端响应回来的字节流. new FileOutputStream("String path");
设置两个头:
* Content-Type: 这是成文件类型的MIME类型: text/css text/javascript : tomcat : web.xml
* Content-Disposition : 默认值: inline 在浏览器当中打开。
文件下载: attachment;filename=xxxx;
下载的细节:
1: 下载文件的名称乱码问题:
通用的解决方案:
new String(name.getBytes("gbk"),"iso-8859-1");
弊端: 该种方式能够解决大部分浏览器, 但是个别的特殊字符不能解决:
不同的浏览器对文件的名称编码方式不一样:
fixfox: Base64 :
其他的浏览器: url
2: 通用性方式: 提供一个工具类到时候再搜吧 :
文件下载 servlet代码如下:
package com.yidongxueyuan.web.servlet;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import sun.misc.BASE64Encoder;
/*
* 负责下载的Servlet:
*/
public class DownLoadFileServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/* request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");*/
/*
* 1: 文件的下载具备一个流: 两个头:
*/
String filename = "C:/Users/Mrzhang/Desktop/API/a练习图片/girl.jpg";
//头信息:
// Content-Type: MIME 文件的MIME类型:
String conentType= this.getServletContext().getMimeType(filename);//能够获得.jpg文件的MIME类型: image/jpeg
String name = "凤姐.jpg";
// String savename = new String(name.getBytes("gbk"),"iso-8859-1"); 解决 大部分情况:
String savename = FilenameEncodingUtils.filenameEncoding(name, request);
String contentDisposition = "attachment;filename="+savename;
//设置响应头信息:
response.setHeader("Content-Type",conentType );
response.setHeader("Content-Disposition", contentDisposition);
//准备的一个流:
//将图片读取到fis当中:
FileInputStream fis = new FileInputStream(filename);
//响应输出流:
ServletOutputStream out = response.getOutputStream();
//流对接: IOUtils
IOUtils.copy(fis, out);
fis.close();
out.close();
}
}
class FilenameEncodingUtils {
// 用来对下载的文件名称进行编码的!
public static String filenameEncoding(String filename, HttpServletRequest request) throws IOException {
String agent = request.getHeader("User-Agent"); //获取浏览器
if (agent.contains("Firefox")) {
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?"
+ base64Encoder.encode(filename.getBytes("utf-8"))
+ "?=";
} else if(agent.contains("MSIE")) {
filename = URLEncoder.encode(filename, "utf-8");
} else {
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}