JavaWeb(12)之文件的上传和下载
文件上传
文件上传的概述
文件上传:将本地的文件通过流写入到服务器的过程。
实际开发中有很多应用:
QQ空间上传图片。
招聘网站上传简历。
文件上传的技术
文件上传的要素
文件上传的三个要素:
- 表单的提交的方式需要是POST
- 表单中需要有 <input type=“file">元素,需要有name属性和值
- 表单enctype=“multipart/form-data”
POST请求的编码格式
Post请求的两种编码格式:application/x-www-form-urlencoded和multipart/form-data
首先我们需要明白在html中的enctype属性,
enctype:规定了form表单在发送到服务器时候编码方式。他有如下的三个值。
- ①application/x-www-form-urlencoded。默认的编码方式。但是在用文本的传输和MP3等大型文件的时候,使用这种编码就显得 效率低下。
- ②multipart/form-data 。 指定传输数据为二进制类型,比如图片、mp3、文件。
- ③text/plain。纯文体的传输。空格转换为 “+” 加号,但不对特殊字符编码。
post默认application/x-www-form-urlencoded方式提交:只能获取获取文件的名字不能获取文件的内容。
multipart/form-data方式提交:可以获取文件的内容
总结:文件上传必须使用multipart/form-data编码方式提交
文件上传的原理分析
JSP页面:
<body><h1>文件上传</h1><!-- 文件上传三要素:* 表单需要POST提交* 表单中需要文件上传项,必须有name的属性和值* 表单的enctype属性必须是multipart/form-data--><form action="" method="post" enctype="multipart/form-data">文件描述:<input type="text" name="info"><br>文件上传:<input type="file" name="upload"><br><input type="submit" value="提交"></form>
</body>
文件上传的案例
第一步:引入文件上传相关的jar包:
common-io下载
common-fileupload下载
第二部:编写文件上传的页面
<body><h1>文件上传</h1><!-- 文件上传三要素:* 表单需要POST提交* 表单中需要文件上传项,必须有name的属性和值* 表单的enctype属性必须是multipart/form-data--><form action="${ pageContext.request.contextPath }/UploadServlet" method="post" enctype="multipart/form-data">文件描述:<input type="text" name="info"><br>文件上传:<input type="file" name="upload"><br><input type="submit" value="提交"></form>
</body>
第三部:编写文件上传的Servlet
FileItem类的常用方法
解决tomcat每次重启丢失项目文件的问题
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;/*** 文件上传的Servlet*/
@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {try {// 1.创建磁盘文件项工厂DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();// 2.创建一个核心的解析类,将磁盘文件项放入ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);// 3.利用核心类解析Request,解析后得到多个请求部分。返回一个LIst集合,List集合装的是每个请求部分的内容(FileItem文件项)。List<FileItem> list = fileUpload.parseRequest(request);// 4.遍历List集合,会得到代表每个部分的文件项的对象。根据文件项判断是否是文件上传项。for (FileItem item : list) {// 判断这个文件是普通项还是文件上传项if (item.isFormField()) {// 普通项// 接收普通项的值:(接收值不在使用request.getParameter())String name = item.getFieldName();// 获得普通项的名称String value = item.getString("UTF-8");// 获得普通项的值System.out.println(name + " " + value);} else {// 文件上传项// 获得文件上传项的文件名称String fileName = item.getName();// IE中获得的是绝对路径,所以需要分割int i = fileName.lastIndexOf("//");if (i != -1) {// 使用的是老版本浏览器fileName = fileName.substring(i + 1);}// 获得文件上传项的数据InputStream is = item.getInputStream();// 获得文件上传的路径:磁盘绝对路径String realPath = getServletContext().getRealPath("/upload");// 创建一个输出流,写入到设置的路径中OutputStream os = new FileOutputStream(realPath + "/" + fileName);// 两个流对接int len = 0;byte[] b = new byte[1024];while ((len = is.read(b)) != -1) {os.write(b, 0, len);}// 释放资源os.close();is.close();}}} catch (Exception e) {e.printStackTrace();}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}
运行结果:
提交后:
控制台
文件夹
文件上传的API
Commons FileUpload 1.4 API文档
DiskFileItemFactory:磁盘文件项工厂
- 构造方法
DiskFileltemFactory()
DiskFileltemFactory(int sizeThreshold,File repostory)
sizeThreshold:设置文件上传的缓冲区的大小,默认值为10kb。
repository:设置文件上传过程中产生临时文件存放的路径。
如果在上传文件的过程中文件的大小超过了缓冲区的大小,就会产生临死文件。
- 方法
这里重点学习两个方法
setSizeThreshold():设置缓冲区的大小
setRepository() :设置临时文件存放的路径
案例:
写在文件上传的Servlet中
// 1.1设置缓冲区大小diskFileItemFactory.setSizeThreshold(1024 * 1024 * 3);// 设置缓存区大小为3M// 1.2设置临时文件存放的路径// 获得临时文件存放的路径String temp = getServletContext().getRealPath("/temp");diskFileItemFactory.setRepository(new File(temp));
运行:
在上传后文件大于缓冲区会在temp文件中存放一个临时文件,这个文件也是可以删除的,在后面会学到。
ServletFileUpload:核心解析类
- 构造方法
我们使用的是有参的构造方法,将FileItemFactory传递进去
- 方法
isMultipartContext:用来判断表单的enctype属性是否正确
parseRequest:解析Request对象,返回一个List集合(每个部分的对象FileItem)
ServletFileUpload类的方法不多,他的父类FileUploadBase方法比较多:
学习几个比较常用的:
设置单个文件的大小
设置上传文件的总大小
设置中文文件名乱码的问题
设置监听文件上传的进度
FileItem文件项
FileItem类的常用方法
方法:
判断表单项是普通项还是文件上传项,如果为true代表是普通项。
普通项的方法:
获得普通项的名称
获得普通项的值
文件上传项的方法:
用来获得文件名的方法
获得文件内容的方法
获得文件的大小
返回文件的类型,后缀名。
删除文件上传过程中的临时文件
JS控制多文件上传
案例需求分析
每点一次添加就添加一个文件上传的模块,点击上传后一起上传。
案例实现
实现多文件上传只需要利用JS代码即可。
然后上传到Servlet即可
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript">function add() {//获得id为div1的元素var div1Element = document.getElementById("div1");div1Element.innerHTML += "<div><input type='file' name='upload'/><input type='button' value='删除' οnclick='del(this)'/></div>"}function del(who) {who.parentNode.parentNode.removeChild(who.parentNode);}
</script>
</head>
<body><h1>多文件上传</h1><form action="${ pageContext.request.contextPath }/UploadServlet" method="post" enctype="multipart/form-data"><input type="button" value="添加" onclick="add()"><input type="submit" value="上传" ><div id="div1"></div></form>
</body>
</html>
文件上传兼容浏览器问题及解决
问题描述
如果使用IE老版本的浏览器出现一个文件名称获取错误问题。
IE老版本获取文件名称的时候,会带有路径。
问题解决
将字符串分割即可
int i = fileName.lastIndexOf("//");if (i != -1) {// 使用的是老版本浏览器fileName = fileName.substring(i + 1);}
文件上传同一个目录下文件同名的问题及解决
问题描述
张三向服务器上传了一个文件aa.txt内容是hello world。李四向服务器上传了一个文件aa.txt内容helloJava。后上传的文件将先上传的文件覆盖了。
问题解决
创建一个工具类:
传递一个文件名,返回一个唯一的文件名
/*** 文件上传的工具类* * @author 25858**/
public class UploadUtils {/*** 传递一个文件名,返回一个唯一的文件名*/public static String getUuidFilename(String fileName) {// 在java的API中有一个类UUID可以产生随机的字符串// UUID.randomUUID().toString();// 获得文件名的扩展名int index = fileName.lastIndexOf(".");String extetions = fileName.substring(index);return UUID.randomUUID().toString() + extetions;}
}
在使用的过程中将文件名传递即可,然后在输出流中使用返回的唯一文件名。
文件上传同一个目录下存放文件过多的问题及解决
问题描述
现在所有的用户都上传文件,如果网站访问量比较大,如果都上传到同一个目录下,在同一个目录下存放的文件太多了,也会对程序有影响(其实打开该目录的时候,都会很卡,更别说读写操作)。
问题解决
分析目录分离算发:
同样将目录分离算发写在工具类中:
/*** 目录分离的算法实现* 两级目录*/public static String getRealPath(String uuidFileName) {int code1 = uuidFileName.hashCode();int d1 = code1 & 0xf;int code2 = code1 >>> 4;int d2 = code2 & 0xf;return "/" + d1 + "/" + d2;}
在Servlet中使用:
将唯一文件名传递,然后组合成新的目录路径,在输出流中使用新的路径。
// 获得文件上传的路径:磁盘绝对路径String realPath = getServletContext().getRealPath("/upload");// 进行目录分离String path = UploadUtils.getRealPath(uuidFileName);String newPath = realPath + path;File file = new File(newPath);// 判断文件是否创建if (!file.exists()) {file.mkdirs();}// 创建一个输出流,写入到设置的路径中FileOutputStream os = new FileOutputStream(newPath + "/" + uuidFileName);
文件下载
文件下载的概述
文件下载:将服务器上的个文件,通过流写入到客户端上。
文件下载的方式
超链接方式下载
超链接方式直接写文件的绝对路径即可
<body><h1>文件下载:超链接的方式</h1><a href="${ pageContext.request.contextPath }/download/111.jpg">111.jpg</a><a href="${ pageContext.request.contextPath }/download/aaa.zip">aaa.zip</a>
</body>
如果浏览器支持这个格式的文件就会直接打开,如果浏览器不支持这个格式的文件才会提示下载。
手动编码方式下载
编写文件下载的页面:
<h1>文件下载:手动编码的方式</h1><a href="${ pageContext.request.contextPath }/DownloadServlet?filename=111.jpg">111.jpg</a><a href="${ pageContext.request.contextPath }/DownloadServlet?filename=aaa.zip">aaa.zip</a>
文件下载的Servlet:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 文件下载的Servlet*/
@WebServlet("/DownloadServlet")
public class DownloadServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 1.接收参数String filename = request.getParameter("filename");// 2.下载:设置两个头和一个流// 设置Context-Type// 获得文件内容类型String type = getServletContext().getMimeType(filename);// 设置发送到客户端的响应时的内容类型response.setContentType(type);// 设置Content-Disposition// 让浏览器收到这份资源的时候, 以下载的方式提醒用户,而不是直接展示。response.setHeader("Content-Disposition", "attachment;filename=" + filename);// 设置一个代表了文件的输入流String path = getServletContext().getRealPath("/download");InputStream is = new FileInputStream(path + "/" + filename);// 使用response获取输出流OutputStream os = response.getOutputStream();// 两个流对接int len = 0;byte[] b = new byte[1024];while ((len = is.read(b)) != -1) {os.write(b, 0, len);}os.close();is.close();}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}
中文文件的下载
创建一个转换中文名称的工具类:
因为中文在提示下载时是显示不出来的,所以要转换一下。
使用时需要将文件名称和HttpServletRequest 传入其中
mport java.io.UnsupportedEncodingException;
import java.net.URLEncoder;import javax.servlet.http.HttpServletRequest;import sun.misc.BASE64Encoder;/*** 转换中文名称的类* * @author 25858**/
public class DownUtils {public static String filenameEncoding(String filename, HttpServletRequest request)throws UnsupportedEncodingException {// 获得请求头中的User-AgentString agent = request.getHeader("User-Agent");// 根据不同的客户端进行不同的编码if (agent.contains("Firefox")) {// 火狐浏览器BASE64Encoder base64Encoder = new BASE64Encoder();filename = "=?UTF-8?B?" + base64Encoder.encode(filename.getBytes("UTF-8")) + "?=";} else {// 其它浏览器filename = URLEncoder.encode(filename, "UTF-8");}return filename;}
}
Servlet类:
在Servlet中使用要注意,将判断浏览器放在获得文件内容类型的下面,还要将输入流中的路径单独创建,因为路径使用的是获取到的正常值,而不是转换后的值。
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import com.wxw.utils.DownUtils;/*** 文件下载的Servlet*/
@WebServlet("/DownloadServlet")
public class DownloadServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 1.接收参数String filename = request.getParameter("filename");// 2.下载:设置两个头和一个流// 设置Context-Type// 获得文件内容类型String type = getServletContext().getMimeType(filename);// 定义一个代表该文件的路径String path = getServletContext().getRealPath("/download");File file = new File(path + "/" + filename);// 判断浏览器并转换中文名称filename = DownUtils.filenameEncoding(filename, request);System.out.println(filename);// 设置发送到客户端的响应时的内容类型response.setContentType(type);// 设置Content-Disposition// 让浏览器收到这份资源的时候, 以下载的方式提醒用户,而不是直接展示。response.setHeader("Content-Disposition", "attachment;filename=" + filename);// 设置一个代表了文件的输入流InputStream is = new FileInputStream(file);// 使用response获取输出流OutputStream os = response.getOutputStream();// 两个流对接int len = 0;byte[] b = new byte[1024];while ((len = is.read(b)) != -1) {os.write(b, 0, len);}os.close();is.close();}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}
案例:给定目录下的文件下载
案例需求
给定一个目录(这个目录可以是任意盘符下的任意路径-这个路径下有多少级目录,每级目录中有多少个文件都是未知的)。
将这个路径中的文件显示到页面上,在页面上给每个问题件都提供响应下载的链接,当点击这个链接的时候,对该文件进行下载。
案例分析
树形数据的遍历和过滤
获取节点的方式:
文件列表显示功能
Java队列(Queue)了解及使用
深入理解FIFO(包含有FIFO深度的解释)
<body><h1>树形遍历</h1><%// 1.创建一个队列Queue<File> queue = new LinkedList<File>();// 2.将根节点入队File root = new File("D:\\壁纸\\壁纸02\\动漫");queue.offer(root);//判断这个队列是否为空,不为空需要遍历while (!queue.isEmpty()) {//将根节点出队File file = queue.poll();//获得更节点下的所有子节点File[] files = file.listFiles();//遍历所以子节点for (File f : files) {//判断此节点是否为叶子节点if (f.isFile()) {%><h4><a href="#"> <%=f.getName()%></a></h4><%} else {queue.offer(f);}}}%>
</body>
下载代码实现
/*** 树形文件下载的代码实现*/
@WebServlet("/DownloadListServlet")
public class DownloadListServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 1.接收参数String path = request.getParameter("filename");File file = new File(path);// 2.下载:设置两个头和一个流// 设置Context-Type// 获得文件内容类型String filename = file.getName();String type = getServletContext().getMimeType(filename);// 设置发送到客户端的响应时的内容类型response.setContentType(type);// 判断浏览器filename = DownUtils.filenameEncoding(filename, request);System.out.println(filename);// 设置Content-Disposition// 让浏览器收到这份资源的时候, 以下载的方式提醒用户,而不是直接展示。response.setHeader("Content-Disposition", "attachment;filename=" + filename);// 设置一个代表了文件的输入流InputStream is = new FileInputStream(file);// 使用response获取输出流OutputStream os = response.getOutputStream();// 两个流对接int len = 0;byte[] b = new byte[1024];while ((len = is.read(b)) != -1) {os.write(b, 0, len);}os.close();is.close();}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}
JavaWeb(12)之文件的上传和下载相关推荐
- 手把手教你学javaweb(五)文件的上传和下载
javaweb项目文件的上传和下载 在进行文件的上传和下载之前,我们先把javaweb项目做一点点的改动,那就是将LoginServlet的跳转由原来的forward方式改为 redirect方式 ...
- JavaWeb 文件的上传和下载
JavaWeb 文件的上传和下载 这是一个简单的案例,让你快速了解JavaWeb中文件的上传和下载 文件的上传和下载是Web开发中非常常见和重要的功能,很多系统中都会经常使用文件的上传和下载. 如:博 ...
- Akka实战:HTTP大文件断点上传、下载,秒传
2019独角兽企业重金招聘Python工程师标准>>> 访问:https://github.com/yangbajing/scala-applications/tree/master ...
- jsp+servlet实现文件的上传和下载
实现文件的上传和下载首先需要理解几个知识,这样才可以很好的完成文件的上传和下载: (1):上传文件是上传到服务器上,而保存到数据库是文件名 (2):上传文件是以文件转换为二进制流的形式上传的 (3): ...
- java struts2下载文件_Struts2下多文件的上传与下载
Struts2下多文件的上传与下载 目录 多文件 上传 下载 随意文件java Struts2单例 配置 动态读取 李顺利 在网络上,对于Java处理文件上传和下载的技术比较多,而Struts作为一款 ...
- Xshell在Windows和Linux间文件的上传和下载
Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/cou ...
- 多类型文件的上传和下载
由于最近在做一个团队项目<办公OA管理系统>,我负责做公共事务,人事管理两大模块,今天做到一个页面要有一个上传和下载文件的功能,功能很简单,就是文件的上传和下载而已,这里只是分享一下过程, ...
- SpringMVC实现文件的上传和下载
SpringMVC实现文件的上传和下载http://www.bieryun.com/1120.html 前些天一位江苏经贸的学弟跟我留言问了我这样一个问题:"用什么技术来实现一般网页上文件的 ...
- 初学Java Web(7)——文件的上传和下载
文件上传 文件上传前的准备 在表单中必须有一个上传的控件 <input type="file" name="testImg"/> 因为 GET 方式 ...
- SpringMVC实现文件的上传与下载
文件的上传与下载可以说是工作中经常使用的功能,现在整理一下,希望能够给大家一个参考.这里以 Maven 的形式来创建项目,相关的配置文件会把主要的内容列出来,其他头文件信息不再一一全部的列出.最后会把 ...
最新文章
- 你哪来这么多事(三):学生信息删除
- Vue2.0---webpack打包知识点-2
- 走近云计算:解密IaaS、PaaS和SaaS
- 【转】排序算法复习(Java实现)(一): 插入,冒泡,选择,Shell,快速排序...
- Java编程思想 学习笔记7
- Jmeter + Grafana + InfluxDB 性能测试监控
- Hyperledger Fabric 核心模块(4)orderer 共识
- 50个jQuery插件可将你的网站带到另一个高度
- COM编程之二 接口
- Angular应用里input字段后面的_ngcontent-hqi是什么含义
- 在带有组合框的值列表的下拉列表中显示显示属性的子集
- 3.0 C++远征:is a
- ubuntu 18.04下安装Java
- ExtJS中listener方法和handler方法的区别
- java anonymous class_java declare anonymous class using proxy api
- 我有一个梦想(I have a dream)
- 教小师妹学多线程,看完我写的例子,脸红成那样!
- DELETE_FAILED_INTERNAL_ERROR 小米之殇
- 继小米机器狗、特斯拉机器人后,小鹏汽车发布智能机器马:真的能骑!
- 怎样提高计算机内存,怎样增加电脑虚拟内存
热门文章
- php基础知识点及留言板功能实现
- 百度超级链AI系列创世藏品发售!
- Mybatis 不等号的表示方法,if……else
- 回收站的东西删了怎么恢复?永久删除的文件如何恢复
- (原創) 如何解決ThinkVantage Power Manager『電源配置』無故消失的bug (NB) (ThinkPad) ?...
- android togglebutton 自定义,Android 自定义ToggleButton
- mysql反引号作用
- 桶装水价格表 it 计算机,桶装水配送价格价格如何计算?「大力水手」
- 几款好用又不贵的BI工具值得推荐
- 给你的宠物做个玩具吧