18.Javaweb中⽂件的上传和下载【重要】
Javaweb中⽂件的上传和下载
⽂件上传
⽂件上传指的是⽤户通过浏览器向服务器上传某个⽂件,服务器接收到该⽂件后会将该⽂件存储在服务器的硬盘中,通常不会存储在数据库中,这样可以减轻数据库的压⼒并且在⽂件的操作上更加灵活,常见的功能是上传头像图⽚。
⽂件上传的原理
所谓的⽂件上传就是服务器端通过request对象获取输⼊流,将浏览器端上传的数据读取出来,保存到服务器端。
⽂件上传的要求
提供form表单,表单的提交⽅式必须是post【get请求装不下那么多】
form表单中的enctype属性必须是 multipart/form-data  【照着做就⾏】
表单中提供input type=”file”上传输⼊域  【⽂件那个表单】
先来个表单:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<figure>
<img src="">
</figure>
<form action="#" method="post" accept-charset="utf-8" enctype="multipart/form-data">  <!--# 提交地址记得改!-->
<input type="file" name="photo"><br>
<input type="submit" value="上传头像">
</form>
</body>
</html>
来个Servlet来接收⼀下这个图⽚:
package upload;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* ⽂件上传例⼦
*/
public class file extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求的输⼊流
InputStream is = InputStream();
//读取输⼊流中的数据
int leng = 0;
byte[] bytes = new byte[1024];
while ((leng = is.read(bytes)) != -1) {
//先打印控制台看看
System.out.println(new String(bytes,0,leng));
}
}
}
打印出来的数据:
------WebKitFormBoundarypM4ZEsxzVdl0NfZV
Content-Disposition: form-data; name="photo"; filename="4-2 鍥剧墖鍒囨崲鏁堟灉[20210508-164643].jpg"
Content-Type: image/jpeg
反正⼀堆乱码但是头部我们是看的懂的就是⼀些标签的属性和上传的照⽚名字!和⽂件类型!
如何解决?请看:
FileUpload⼯具的使⽤
在实际开发中通常会借助第三⽅⼯具来实现上传功能,应⽤较多的是apache旗下的Commons-fileupload。【第三⽅插件都要导jar包!学了那么久应该知道吧】
Apache 开源组织提供了⼀个⽤来处理表单⽂件上传的⼀个开源组件( Commons-fileupload ),该组
件性能优异,并且其API使⽤极其简单,可以让开发⼈员轻松实现web⽂件上传功能,因此在web开发中实现⽂件上传功能,通常使⽤Commons-fileupload组件实现。
在使⽤该⼯具实现上传功能时,⾸先需要导⼊Commons-fileupload和commons-io两个jar包。【这两个包可以处理表单和处理上传⽂件】
去百度阿帕奇官⽹下载即可:
然后我们接着上⾯的例⼦,直接上⼿:
先提⽰⼀下如果不能在Eclipse中服务器不能创建⽂件夹,那么就在换成本地的Tomcat的即可。
file.jsp:
package upload;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.management.RuntimeErrorException;
import javax.print.attribute.standard.Severity;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apachemons.fileupload.FileItem;
import org.apachemons.fileupload.FileUploadException;
import org.apachemons.fileupload.disk.DiskFileItemFactory;
import org.apachemons.fileupload.servlet.ServletFileUpload;
/**
* ⽂件上传例⼦
*/
public class file extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//先来个编码防⽌⽂件名乱码推荐在过滤器写
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
//1.判断表单是否⽀持⽂件上传。即:enctype="multipart/form-data"
boolean ismultipart = ServletFileUpload.isMultipartContent(request); //别导错包了是你jar那个包哦
if(!ismultipart) {
//不⽀持⽂件上传的话那就报异常
throw new RuntimeException("err:还请求不⽀持⽂件上传!");
}
//2.创建⼀个DiskFileItemfactory 对象
DiskFileItemFactory dff = new DiskFileItemFactory();
//3.创建⼀个 ServletFileUpload对象,这个是上传对象的核⼼。
ServletFileUpload sfu = new ServletFileUpload(dff);
/
/4.解析request对象并得到⼀个表单项的集合
//sfu.parseRequest 解析参数是request  List<FileItem> 类型:
List<FileItem> fileitme;
try {
fileitme = sfu.parseRequest(request);    //需要处理异常
for(FileItem t : fileitme) {
if(t.isFormField()) {
//普通表单【直接打印】:
String fileName = t.getFieldName();    //字段名
String fileValue = t.getString("UTF-8");    //字段值
System.out.println(fileName  + " : " + fileValue);
}else {
//⽂件表单【保存到服务器的硬盘中】:
//获取⽂件名
String fileName = t.getName();
//获取输⼊流
InputStream is = t.getInputStream();
//创建输出流获取地址 -> ⽂件对象 -> 输出流 -> 输出
String path = ServletContext().getRealPath("/head_img");    //getRealPath:动态的得到WebRoot中⽂件在磁盘中的位置
System.out.println("*调试:存放⽂件路径:" + path);    //console
File file = new File(path,fileName);        //这个看不懂你可能得倒回去学JavaSe 了....
FileOutputStream fos = new FileOutputStream(file);
//开始输出
byte[] bytes = new byte[1024];
int len = -1;
while((len = is.read(bytes)) != -1) {
fos.write(bytes, 0, len);;
}
/*
临时⽂件⼀旦⽤完,就可将其删除了,否则占⽤服务器的硬盘空间。
需要注意的是,对于临时⽂件的删除,需要在 IO 流关闭后,否则,⽆法删除。
*/
is.close();
fos.close();
t.delete();    //删除刚上传的临时⽂件
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
主要还是步骤要熟悉还有就是⼏个⽅法要记住特别是路径要分清楚
允许后你会发现你的⽬录下  xxxx.head_img ⾥⾯就是你放的图⽚【头像】了
xxxx.head_img 这个⽬录⼀定要存在服务器根部⽬下!【报错的时候会给出你可以复制然路劲跟进去粘贴即可】经过我完全测试终于发现了FileItem【 fileitme 类的返回值【List包裹的那个】】的⼏个常⽤⽅法:【要熟悉】所以说⽤这两个jar包很⽅便直接处理了⽂件的还有表单的。
1.  boolean isFormField() 【这个ServletFileUpload 也有⼀开始就是⽤ServletFileUpload的!】
⽤于判断FileItem类对象封装的数据是⼀个普通⽂本表单字段,还是⼀个⽂件表单字段,如果是普通表单字段则返回true,否则返回false。
2.    String getName()
⽤于获得⽂件上传字段中的⽂件名,注意IE或FireFox中获取的⽂件名是不⼀样的,IE中是绝对路径,FireFox中只是⽂件名。
3.String getFieldName()
⽤于返回表单标签name属性的值。
4.void write(File file)
将FileItem对象中保存的主体内容保存到某个指定的⽂件中。如果FileItem对象中的主体内容是保存在某个临时⽂件中,该⽅法顺利完成后,临时⽂件有可能会被清除。该⽅法也可将普通表单字段内容写⼊到⼀个⽂件中,但它主要⽤途是将上传的⽂件5.String getString()
将FileItem对象中保存的数据流内容以⼀个字符串返回,它有两个重载的定义形式:
public Java.lang.String getString()
public java.lang.String getString(java.lang.String encoding)
即⼀个不指定编码⼀个指定编码
6.String getContentType()
⽤于获得上传⽂件的类型,即表单字段元素描述头属性“Content-Type”的值,如“image/jpeg”。如果FileItem类对象对应的是普通表单字段,该⽅法将返回null
7.boolean isInMemory()
⽤来判断FileItem对象封装的数据内容是存储在内存中,还是存储在临时⽂件中,如果存储在内存中则返回true,否则返回false。
8. delete()
delete⽅法⽤来清空FileItem类对象中存放的主体内容,如果主体内容被保存在临时⽂件中,delete⽅法将删除该临时⽂件。
9.InputStream getInputStream()
以流的形式返回上传⽂件的数据内容。
10.long getSize()
返回该上传⽂件的⼤⼩(以字节为单位)。
关于优化: 上⾯⽤到了上传下⾯进⾏优化说明:
1.⽂件名乱码你可以在过滤器过滤编码或者post头过滤
2.⽂件夹不存在  ⽂件夹不存在你可以加判断语句即可
3. 这个jar包不是可以判断⽂件表单普通表单吗那你可以直接⽤来获取即可【具体功能⾃⼰发挥】
4.你可以拓展完成⽹页显⽰该图⽚,例如更换头像.
5.加判断是否没选择⽂件就点击了上传【会抛异常】
注意:⽂件上传时在服务器中所存放的路径最好是⽤户不能直接访问的路径,这样可以保证⽂件的安全。
⽤Commons-fileupload和commons-io 上传⽂件的原理:
临时⽬录:
临时⽬录
⽂件由浏览器通过⽹络上传到服务器,并不是直接通过⼀条⽹络线路将所有请求数据发送到了服务器的。⽽是将这些数据分为了很多个数据包,这些数据包分别被编号后,经由
不同的⽹络线路最终发送到了服务器中。这些数据包到达服务器的时间会根据不同的⽹络线路的情况不同,分别先后到达服务器,顺序是不定的。因此服务器会在其临时⽬录
中,创建⼀个临时⽂件,将这些数据包进⾏拼接组装。
Tomcat 默认情况下的临时⽬录是 Tomcat 服务器安装⽬录的 temp ⼦⽬录。我们也可以修改临时⽬录的默认位置。Apache 的 FileUpload ⽀持设置创建临时⽂件的最⼩临界值
【Tomcat的默认边界是10kb】,即只有上传的⽂件⼤⼩超出这个值,才会创建临时⽂件。通过DiskFileItemFactory 的 setSizeThreshold()⽅法可以设置临界值,单位为字节。
通过DiskFileItemFactory 的 setRepository()⽅法可以指定临时⽬录。
设置了临时⽂件的 Servlet:
package upload;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.management.RuntimeErrorException;
import javax.print.attribute.standard.Severity;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apachemons.fileupload.FileItem;
import org.apachemons.fileupload.FileUploadException;
import org.apachemons.fileupload.disk.DiskFileItemFactory;
import org.apachemons.fileupload.servlet.ServletFileUpload;
下载apache/**
* ⽂件上传例⼦
*/
public class file extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//先来个编码防⽌⽂件名乱码推荐在过滤器写
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
//设置好上传的图⽚需要放的位置
String img_path = ServletContext().getRealPath("/head_img");    //getRealPath:动态的得到WebRoot中⽂件在磁盘中的位置
System.out.println("*调试:存放⽂件路径:" + img_path);    //console
//1.判断表单是否⽀持⽂件上传。即:enctype="multipart/form-data"
boolean ismultipart = ServletFileUpload.isMultipartContent(request); //别导错包了是你jar那个包哦
if(!ismultipart) {
//不⽀持⽂件上传的话那就报异常
throw new RuntimeException("err:还请求不⽀持⽂件上传!");
}
//        *********************************//
//2.创建⼀个DiskFileItemfactory 对象
DiskFileItemFactory dff = new DiskFileItemFactory();
//设置临时⽂件的⽬录
String temp_path = ServletContext().getRealPath("/temp");    //确保你temp存在啊
dff.setRepository(new File(temp_path));        //设置指定临时⽂件的路径默认是tomcat的temp
dff.setSizeThreshold(1024*1024*3);        //设置临时⽂件触发⼤⼩【单位:字节】这⾥设置3MB
//        *********************************//
//3.创建⼀个 ServletFileUpload对象,这个是上传对象的核⼼。
ServletFileUpload sfu = new ServletFileUpload(dff);
//4.解析request对象并得到⼀个表单项的集合
//sfu.parseRequest 解析参数是request  List<FileItem> 类型:
List<FileItem> fileitme;
try {
fileitme = sfu.parseRequest(request);    //需要处理异常
for(FileItem t : fileitme) {
if(t.isFormField()) {
//普通表单【直接打印】:
String fileName = t.getFieldName();    //字段名
String fileValue = t.getString("UTF-8");    //字段值
System.out.println(fileName  + " : " + fileValue);
}else {
//⽂件表单【保存到服务器的硬盘中】:
//获取⽂件名
String fileName = t.getName();
//获取输⼊流
InputStream is = t.getInputStream();
//创建输出流获取地址 -> ⽂件对象 -> 输出流 -> 输出
File file = new File(img_path,fileName);        //这个看不懂你可能得倒回去学JavaSe 了....
FileOutputStream fos = new FileOutputStream(file);
//开始输出
byte[] bytes = new byte[1024];
int len = -1;
while((len = is.read(bytes)) != -1) {
fos.write(bytes, 0, len);;
}
is.close();
fos.close();
//                        t.delete();    //清空临时⽂件执⾏前请先关闭流【记得清除掉啊!】
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
上传⽂件名重名的问题
在服务器中已有⽂件名叫做s.jpg的⽂件,当⽤户再次上传同名的⽂件时,会将之前的⽂件名覆盖,这样会出现问题。为了解决该问题,可以将上传的⽂件重命名,有两种⽅式:
  必须⼆选⼀!
在⽂件名中添加系统时间戳。
在⽂件名中添加uuid。【常⽤】
uuid是Universally Unique Identifier的缩写,中⽂是通⽤统⼀识别码,uuid具有唯⼀性,uuid的⽣成跟系统的时间、mac地址、时间序列、随机数有关,所以通常所⽣成的uuid是不会重复的,两个相同的uuid出现的概率⾮常低(⽐陨⽯撞击地球的概率还要低)。
部分代码⽰例:
//⽂件表单【保存到服务器的硬盘中】:
//获取⽂件名
String fileName = t.getName();
//获取输⼊流
InputStream is = t.getInputStream();
//创建输出流获取地址 -> ⽂件对象 -> 输出流 -> 输出
⼆选⼀!
fileName = System.currentTimeMillis() + "_" + fileName;    //加⼊时间戳防⽌⽂件名冲突【⽐较少⽤】
fileName =  UUID.randomUUID() + "_" + fileName;            //加⼊UUID 【常⽤】
System.out.println(fileName);
File file = new File(img_path,fileName);        //这个看不懂你可能得倒回去学JavaSe 了....
FileOutputStream fos = new FileOutputStream(file);
//开始输出
byte[] bytes = new byte[1024];
int len = -1;
while((len = is.read(bytes)) != -1) {
fos.write(bytes, 0, len);;
}
is.close();
fos.close();
t.delete();    //清空临时⽂件执⾏前请先关闭流
}
}
上传⽂件的⼤⼩
对于上传⽂件的⼤⼩,可以通过ServletFileUpload 的 setFileSizeMax()与 setSizeMax()⽅法进⾏控制。setFileSizeMax()⽤于设置单个⽂件上传的最⼤值,⽽setSizeMax()⽤于设置单次【包含多个】上传的最⼤值。即若⼀次上传多个⽂件,每个⽂件的⼤⼩边界值与所有⽂件加起来的最⼤⼩值。
部分代码演⽰:
..... 
     //3.创建⼀个 ServletFileUpload对象,这个是上传对象的核⼼。
ServletFileUpload sfu = new ServletFileUpload(dff);
//设置上传⽂件的⼤⼩限制:
sfu.setFileSizeMax(1024*1024*2);    //设置上传单个⽂件限制2M
sfu.setSizeMax(1024*1024*5);        //设置上传⽂件总⼤⼩限制5M
//PS:超过会报异常且不会上传!
//4.解析request对象并得到⼀个表单项的集合
.....
因为⽤到 ServletFileUpload  所以推荐在他后⾯设置
创建⽬录
⽆论是 Windows 系统、Linux 系统,还是其它系统,其⽬录中所包含的⽂件数量是有上
限的。所以对于上传的⽂件,应该分⽬录进⾏管理。若⽂件较多,则可按照年、⽉、⽇创建多级⼦⽬
录。这样,即⽅便管理,⼜不会超出⽬录的⽂件数量上限。
部分演⽰代码:
//⽂件表单【保存到服务器的硬盘中】:
//获取⽂件名
String fileName = t.getName();
//获取输⼊流
InputStream is = t.getInputStream();
//创建输出流获取地址 -> ⽂件对象 -> 输出流 -> 输出
//下⾯开始创建多层⽂件夹【按⽇期创】
LocalDate date_now = w();
int Year = Year();
int Month = MonthValue();
int Day = DayOfMonth();
String day_path = "/" + Year + "/" + Month + "/" + Day;
//创建⽗⽬录【年/⽉/⽇】
File parentPath = new File(img_path + day_path);        //【上⾯已定义img_path】
//如果不存在则创建:
if (!ists()) {
parentPath.mkdirs();
}
fileName += "_" + System.currentTimeMillis();    //加⼊时间戳防⽌⽂件名冲突【⽐较少⽤】
fileName +=  "_" + UUID.randomUUID();            //加⼊UUID 【常⽤】
System.out.println(fileName);
File file = new File(parentPath,fileName);        //这个看不懂你可能得倒回去学JavaSe 了....
FileOutputStream fos = new FileOutputStream(file);
//开始输出
byte[] bytes = new byte[1024];
int len = -1;
while((len = is.read(bytes)) != -1) {
fos.write(bytes, 0, len);;
}
is.close();
fos.close();
t.delete();    //清空临时⽂件执⾏前请先关闭流
}
  此处分块简介功能,但是实战开发的时候⼀定要组合好代码不要东⼀块西⼀块实例化的放⼀块然后要创建⽬录的放⼀块要合并⽬录名的放⼀块..........总⽽⾔之:
看着代码全部功能来做⼀个整体的排版【最佳最简洁⾼效】
  关于路径主要还是这个⽅法:
//getRealPath:动态的得到WebRoot中⽂件在磁盘中的位置

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。