文件上传的原理
你如果要去一个网站,如果发一个请求或文件下载,
电脑为客户端, 服务器为s端,服务器中有网站,web应用,还有文件操作系统File System
IO流,两个池子或许穿东西需要一条管道,两台不同电脑之间的管道就是网络
网络中存在协议,协议就是为了传东西的
传输文件需要响应头和响应体,想要传输文件或者下载文件咱们也要设置固定的响应头和响应体
上传文件首先让浏览器能够支持文件上传 input(file)
input file 通过网络上传到web应用,再从web应用传到文件系统
http 80
https 443
URL:指向互联网上的一个具体的地址
NetWork网络传输如何写?
写网络传输 工程十分浩大,所以我们直接用现有的java类
文件上传的注意事项
- 为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下
- 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名(-时间戳 -uuid -md5 -位运算)
- 要限制上传文件的最大值
- 可以限制上传文件的类型,在收到上传文件名时,判断后缀是否合法
上传文件需要用到的类
需要用到的类
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象,在使用
ServletFileUpload对象解析请求时需要DiskFileItemFactory对象。所以,我们需要在进行解析工作前构造好
DiskFileItemFactory对象,通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象
的fileItemFactory属性
ServletFileUpload类
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象中,使用其parseRequest(HttpServletRequest)方法
可以将通过表单中每一个HTML标签提交的数据封装成一个FileItem对象,然后以List列表的形式返回,使用该方法处理上传文件简单易用
trim()方法去掉首尾空格
实现过程
前端提交form表导致FileServlet中
我们在FileServlet中实现上传文件的功能
1.首先
判断上传的文件是普通表单还是带文件的表单
if(!ServletFileUpload.isMultipartContent(request)){
return;//终止方法运行,说明这是一个普通的表单,直接返回
}
2.创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件
String uploadPath=this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile=new File(uploadPath);
if(!uploadFile.exists()){
uploadFile.mkdir();//创建这个目录
}
System.out.println("创建目录成功");
3.缓存,临时文件:文件超过规定大小,被弄成临时文件,过几天自动删除,或者提醒用户转存为永久
String tmpPath=this.getServletContext().getRealPath("/WEB-INF/upload");
File file=new File(tmpPath);
if(!file.exists()){
file.mkdir();//创建这个目录
}
4.处理上传的文件,一般需要通过流来获取,我们可以使用request.getInputStream(),原生态的文件上传流获取,十分麻烦
但是我们都建议使用Apache的文件上传组件来实现,common-fileupload,它需要依赖于commons-io组件:
🌟创建DiskFileItemFactory对象,处理文件上传路径或者大小限制的:
DiskFileItemFactory factory=new DiskFileItemFactory();
通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中:
factory.setSizeThreshold(1024*1024);
actory.setRepository(file);
DiskFileItemFactory factory=getDiskFileItemFactory(file);
//======================================================
DiskFileItemFactory getDiskFileItemFactory(File file){
DiskFileItemFactory factory=new DiskFileItemFactory();
//通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中:
factory.setSizeThreshold(1024*1024);
factory.setRepository(file);
return factory;
}
5.获取ServletFileload 用来监听
监听文件的上传进度
ServletFileUpload upload=getServletFileUpload(factory);
//=====================================================
ServletFileUpload getServletFileUpload(DiskFileItemFactory factory){
ServletFileUpload upload=new ServletFileUpload(factory);
//🌟监听文件上传的进度
upload.setProgressListener(new ProgressListener() {
@Override
//pBytesRead:已读取到的文件大小
//pContentLength:文件大小
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("总大小:"+pContentLength+"已上传:"+pBytesRead);
}
});
//处理乱码问题
upload.setHeaderEncoding("UTF-8");
//设置单个文件的最大值
upload.setFileSizeMax(1024*1024*10);
//设置总共能够上传文件的大小
//1024=1kb*1024=1M*10=10M
upload.setSizeMax(1024*1024*10);
return upload;
}
6.处理上传文件
String msg=uploadParseRequest(upload,request,uploadPath);
//=======================================================
String uploadParseRequest(ServletFileUpload upload, HttpServletRequest request,String uploadPath){
String msg="上传失败";
try {
List<FileItem> fileItems=upload.parseRequest(request);
//fileItem每一个表单对象
for (FileItem fileItem : fileItems) {
//判断上传的文件是普通的表单还是带文件的表单
if(fileItem.isFormField()){
//getFiledName指的是前端表单控件的name;
String name=fileItem.getFieldName();
String value=fileItem.getString("UTF-8");
System.out.println(name+":"+value);
}else{
//如果是文件表单
//1.处理文件=================
String uploadFileName=fileItem.getName();
System.out.println("上传的文件名:"+uploadFileName);
//可能存在文件名不合法的情况
if(uploadFileName.trim().equals("")||uploadFileName==null){
continue;
}
//获得上传的文件名 /images/gurl/paojie.png
String fileName=uploadFileName.substring(uploadFileName.lastIndexOf("/")+1);
//获得文件的后缀名
String fileExtName=uploadFileName.substring(uploadFileName.lastIndexOf(".")+1);
/*
* 如果文件的后缀名fileExtName不是我们所要的
* 就直接return 不处理,告诉用户文件类型不对
* */
//可以使用UUID(唯一识别的通用码),保证文件名唯一
//UUID.randomUUID() 随机生成一个唯一识别的通用码
//网络传输中的东西都需要序列化,
//POJO,实体类只能在本机上用,如果想要在多个电脑上运行,传输===》需要把对象都序列化了
//implements Serializable 没有方法的接口-》标记接口。JVM虚拟机在运行的时候,JVM的本地方法栈-- native->c++
//JNI=java Native Interface->java 本地化接口
//Java栈
//UUID 通过位运算,生成随机序列号保证不重复性
String uuidPath = UUID.randomUUID().toString();
//2.存放地址=================
//存到哪?uploadPath
//文件真实存在的路径realPath
String realPath=uploadPath+"/"+uuidPath;
//给每个文件创建一个对应的文件夹
File realPathFile=new File(realPath);
if(!realPathFile.exists()){
realPathFile.mkdir();
}
//3.文件传输=================
//获取文件上传的流
InputStream inputStream=fileItem.getInputStream();
//创建一个文件输出流
//realPath=真实的文件夹
//差了一个文件,加上输出文件的名字+"/"+uuidFileName
FileOutputStream fos=new FileOutputStream(realPath+"/"+fileName);
//创建一个缓冲区
byte[] buffer=new byte[1024*1024];
//判断是否读取完毕
int len=0;
//如果大于0说明还存在数据
while((len=inputStream.read(buffer))>0){
fos.write(buffer,0,len);
}
msg="上传文件成功";
//关闭流
fos.close();
inputStream.close();
fileItem.delete();//上传文件成功,清除临时文件
}
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return msg;
}
7.最后得到文件上传的结果利用Servlet请求转发
//servlet请求转发消息
request.setAttribute("msg",msg);
request.getRequestDispatcher("info.jsp").forward(request,response);
完整代码
package com.lding.servlet;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.List;
import java.util.UUID;
/**
* @program: FileTransport
* @description: ${description}
* @author: 王丁
* @date: 2021-11-11 11:25
**/
public class FileServlet extends javax.servlet.http.HttpServlet {
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
System.out.println("进入FileServlet");
//判断上传的文件是普通表单还是带文件的表单
if(!ServletFileUpload.isMultipartContent(request)){
return;//终止方法运行,说明这是一个普通的表单,直接返回
}
//创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件
String uploadPath=this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile=new File(uploadPath);
if(!uploadFile.exists()){
uploadFile.mkdir();//创建这个目录
}
System.out.println("创建目录成功");
//缓存,临时文件
//文件超过规定大小,被弄成临时文件,过几天自动删除,或者提醒用户转存为永久
String tmpPath=this.getServletContext().getRealPath("/WEB-INF/upload");
File file=new File(tmpPath);
if(!file.exists()){
file.mkdir();//创建这个目录
}
//处理上传的文件,一般需要通过流来获取,我们可以使用request.getInputStream(),原生态的文件上传流获取,十分麻烦
//但是我们都建议使用Apache的文件上传组件来实现,common-fileupload,它需要依赖于commons-io组件:
/*
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileTtem对象
在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象
所以,我们需要在进行解析工作前构造好DiskFileItemFactory对象
通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象
对象的fileItemFactory属性
*/
DiskFileItemFactory factory=getDiskFileItemFactory(file);
// //🌟监听文件上传的进度
ServletFileUpload upload=getServletFileUpload(factory);
//3.🌟处理上传文件
String msg=uploadParseRequest(upload,request,uploadPath);
//servlet请求转发消息
request.setAttribute("msg",msg);
request.getRequestDispatcher("info.jsp").forward(request,response);
}
DiskFileItemFactory getDiskFileItemFactory(File file){
DiskFileItemFactory factory=new DiskFileItemFactory();
//通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中:
factory.setSizeThreshold(1024*1024);
factory.setRepository(file);
return factory;
}
ServletFileUpload getServletFileUpload(DiskFileItemFactory factory){
ServletFileUpload upload=new ServletFileUpload(factory);
//🌟监听文件上传的进度
upload.setProgressListener(new ProgressListener() {
@Override
//pBytesRead:已读取到的文件大小
//pContentLength:文件大小
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("总大小:"+pContentLength+"已上传:"+pBytesRead);
}
});
//处理乱码问题
upload.setHeaderEncoding("UTF-8");
//设置单个文件的最大值
upload.setFileSizeMax(1024*1024*10);
//设置总共能够上传文件的大小
//1024=1kb*1024=1M*10=10M
upload.setSizeMax(1024*1024*10);
return upload;
}
String uploadParseRequest(ServletFileUpload upload, HttpServletRequest request,String uploadPath){
String msg="上传失败";
try {
List<FileItem> fileItems=upload.parseRequest(request);
//fileItem每一个表单对象
for (FileItem fileItem : fileItems) {
//判断上传的文件是普通的表单还是带文件的表单
if(fileItem.isFormField()){
//getFiledName指的是前端表单控件的name;
String name=fileItem.getFieldName();
String value=fileItem.getString("UTF-8");
System.out.println(name+":"+value);
}else{
//如果是文件表单
//1.处理文件=================
String uploadFileName=fileItem.getName();
System.out.println("上传的文件名:"+uploadFileName);
//可能存在文件名不合法的情况
if(uploadFileName.trim().equals("")||uploadFileName==null){
continue;
}
//获得上传的文件名 /images/gurl/paojie.png
String fileName=uploadFileName.substring(uploadFileName.lastIndexOf("/")+1);
//获得文件的后缀名
String fileExtName=uploadFileName.substring(uploadFileName.lastIndexOf(".")+1);
/*
* 如果文件的后缀名fileExtName不是我们所要的
* 就直接return 不处理,告诉用户文件类型不对
* */
//可以使用UUID(唯一识别的通用码),保证文件名唯一
//UUID.randomUUID() 随机生成一个唯一识别的通用码
//网络传输中的东西都需要序列化,
//POJO,实体类只能在本机上用,如果想要在多个电脑上运行,传输===》需要把对象都序列化了
//implements Serializable 没有方法的接口-》标记接口。JVM虚拟机在运行的时候,JVM的本地方法栈-- native->c++
//JNI=java Native Interface->java 本地化接口
//Java栈
//UUID 通过位运算,生成随机序列号保证不重复性
String uuidPath = UUID.randomUUID().toString();
//2.存放地址=================
//存到哪?uploadPath
//文件真实存在的路径realPath
String realPath=uploadPath+"/"+uuidPath;
//给每个文件创建一个对应的文件夹
File realPathFile=new File(realPath);
if(!realPathFile.exists()){
realPathFile.mkdir();
}
//3.文件传输=================
//获取文件上传的流
InputStream inputStream=fileItem.getInputStream();
//创建一个文件输出流
//realPath=真实的文件夹
//差了一个文件,加上输出文件的名字+"/"+uuidFileName
FileOutputStream fos=new FileOutputStream(realPath+"/"+fileName);
//创建一个缓冲区
byte[] buffer=new byte[1024*1024];
//判断是否读取完毕
int len=0;
//如果大于0说明还存在数据
while((len=inputStream.read(buffer))>0){
fos.write(buffer,0,len);
}
msg="上传文件成功";
//关闭流
fos.close();
inputStream.close();
fileItem.delete();//上传文件成功,清除临时文件
}
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return msg;
}
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
}
}