1. 文件上传
1.1 准备
前提
- 提供form表单,method必须是POST
- form表单的enctype必须是multipart/form-data
- 若使用默认的,则只能获取到文件名
- 提供input type="file"类的上传输入域
enctype属性
enctype作用:告知服务器请求正文的MIME类型。(请求消息头:与Content-Type作用是一致的)
可选值:
- application/x-www-form-urlencoded(默认): 正文:username=admin&password=123 服务器获取数据:String username = request.getParameter(“username”);
- multipart/form-data: 正文:
服务器获取数据:不能通过request.getParameter(String)方法获取指定的表单字段字符内容,因为文件上传表单已经不在是字符内容,而是字节内容,需要通过IO流获取。
1.2 案例 (了解)
html
Servlet
这种办法获取到的数据会带分割串,处理比较麻烦,因此一般使用第三方包
1.3 fileupload (重点)
1.3.1 简介
fileupload是由apache的commons组件提供的上传组件。 它最主要的工作就是帮我们解析request.getInputStream()。
使用fileupload只需要2个jar包
- commons-fileupload.jar,核心包;
- commons-io.jar,依赖包。
fileupload的核心类有:
- DiskFileItemFactory
- ServletFileUpload
- FileItem
1.3.2 解析原理
1.3.3 FileItem对象
FileItem对象对应一个表单项(表单字段)。可以是文件字段或普通字段
- boolean isFormField():判断当前表单字段是否为普通文本字段,如果返回false,说明是文件字段;
- String getFieldName():获取字段名称,例如:,返回的是username;
- String getString():获取字段的内容,如果是文件字段,那么获取的是文件内容,当然上传的文件必须是文本文件;
- String getName():获取文件字段的文件名称;(a.txt)
- String getContentType():获取上传的文件的MIME类型,例如:text/plain。
- int getSize():获取上传文件的大小;
- InputStream getInputStream():获取上传文件对应的输入流;
- void write(File):把上传的文件保存到指定文件中。
- delete():删除临时文件
1.3.4 使用fileupload
代码语言:javascript复制@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException,IOException {
//1.判断是否为文件上传表单
if(ServletFileUpload.isMultipartContent(req)){
//2.创建个文件工厂
FileItemFactory factory = new DiskFileItemFactory();
//3.创建文件上传对象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//4.解析请求
try{
List<FileItem> items = fileUpload.parseRequest(req);
for(FileItem item : items){
if(item.isFormField()){//普通表单字段
processFormField(item);
}else{//文件字段
processFile(item);
}
}
}catch (FileUploadException fue){
fue.printStackTrace();
}
}
}
private void processFile(FileItem item) {
String fieldName = item.getFieldName();
String fieldValue = item.getString();
//1.获取文件扩展名
String suffix = item.getName().split("\.")[1];
String saveFileName = UUID.randomUUID().toString() "." suffix;
System.out.println(saveFileName ":" item.getContentType());
//2.创建文件保存的文件夹
String folderPath = this.getServletContext().getRealPath("/WEB-INF/upload");//获取项目真实路径
System.out.println("保存的路径:" folderPath);
File file = new File(folderPath);
if(!file.exists()){
file.mkdirs();
}
//3.保存文件
//文件完整保存路径
String filePath = folderPath "/" saveFileName;
try {
item.write(new File(filePath));
item.delete();//删除临时文件
} catch (Exception e) {
e.printStackTrace();
}
}
private void processFormField(FileItem item) {
String fieldName = item.getFieldName();
String fieldValue = item.getString();
System.out.println(fieldName ":" fieldValue);
}
}
1.4 保存路径配置
1.4.1 添加日期目录
1.4.2 将日期转换为16进制符
上述方法源自视频教学,但是似乎有误。 可能是每个Date对象都有不同的hashCode造成的,改为:将Date赋值给String对象,使用String创建hashcode
1.5 注意事项
1.5.1 中文乱码
表单普通字段乱码用item.getString(“UTF-8”);
文件名乱码可用
1.5.2 文件上传大小限制
单个文件大小:
- ServletFileUpload.setFileSizeMax(字节)
总文件大小:(多文件上传)
- ServletFileUpload.setSizeMax(字节)
1.5.3 临时文件问题
DiskFileItemFactory:
- 作用:产生FileItem对象
- 内部有一个缓存,缓存大小默认是10Kb。如果上传的文件超过10Kb,用磁盘作为缓存。
存放缓存文件的目录在哪里? 默认是系统的临时目录。
如果自己用IO流实现的文件上传,要在流关闭后,清理临时文件。 在最后加上FileItem.delete(),删除临时文件
1.6 多文件上传
动态添加
代码语言:javascript复制<form action="/UploadServlet" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username" placeholder="请输入用户名"><br>
密码:<input type="password" name="password" placeholder="请输入密码"><br>
<div id="div1">
<div>
<input type="file" name="photo" /><input type="button" value="添加" onclick="addFile()"/><br />
</div>
</div>
<input type="submit" value="提交">
</form>
代码语言:javascript复制<script type="text/javascript">
function addFile(){
//得到div容器
var div1 = document.getElementById("div1");
div1.innerHTML = "<div><input type='file' name='photo' /><input type='button' value='删除' οnclick='delFile(this)'/><br /></div>";
}
function delFile(input){
//使用input对象的爷爷删除他的爸爸
input.parentNode.parentNode.removeChild(input.parentNode);
}
</script>
2. 文件下载
2.1 响应头设置
文件下载需要设置两个响应头
- Content-Disposition attachment;filename=xx.png 以附件形式下载
- Content-Type application/octet-stream 二进制流
2.2 案例
代码语言:javascript复制@WebServlet("/DownloadServlet")
public class DownloadServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//1.文件下载的位置
//此处可通过获取客户端参数然后在数据库中查找是否存在文件
String filePath = "C:/test.jpg";
//2.文件名
String fileName = "img.jpg";
fileName = URLEncoder.encode(fileName,"UTF-8");//中文名文件需要进行URL编码
//3.设置响应头
resp.setHeader("Content-Disposition","attachment;filename=" fileName);
resp.setContentType("application/octet-stream");
//4.响应文件
File file = new File(filePath);
InputStream is = new FileInputStream(file);
byte[] bytes = new byte[1024 * 8];
int len;
OutputStream os = resp.getOutputStream();
while((len = is.read(bytes)) != -1){
os.write(bytes,0,len);
}
//5.关闭流
is.close();
os.close();
}
}
3. 总结
实质上,就是IO流的使用 由于文件在网络上通过字节流传输,因此通过字节IO流实现即可