文件的上传

文件的上传

文件上传的概述

问题:什么是文件上传?为什么使用文件上传?

  • 就是将客户端资源,通过网络传递到服务器端。
  • 就是因为数据比较大,我们必须通过文件上传才可以完成将数据保存到服务器端操作.
  • 文件上传的本质:就是IO流的操作。

演示:文件 上传应该 怎样操作?

	浏览器端:
		1.method=post 只有post才可以携带大数据
		2.必须使用<input type='file' name='f'>要有name属性
		3.encType="multipart/form-data"
	
	服务器端:
		request对象是用于获取请求信息。
		它有一个方法  getInputStream(); 可以获取一个字节输入流,通过这个流,可以读取到
		所有的请求正文信息.

文件上传原理:

  • 浏览器端注意上述三件事,在服务器端通过流将数据读取到,在对数据进行解析.
  • 将上传文件内容得到,保存在服务器端,就完成了文件上传。

文件上传的体验

文件上传需要用到下面的组件

<input type="file" name="f" ><br>

并且在form表单中要使用

method="post" encType="multipart/form-data"
  • method="post"是必要的因为是大数据的传输,

  • encType="multipart/form-data"的原因是。不然只能取到输入框中的信息,f=C%3A%5CUsers%5CAdministrator%5CDesktop%5Ca.txt

  • 设置encType="multipart/form-data"之后的效果:

在这里插入图片描述

编写上传文件的表单:

upload1.jsp

<body>
	<form action="${pageContext.request.contextPath}/upload1" method="post" encType="multipart/form-data">
		普通文本框:<input type="text" name="content"><br>
		文件上传:<input type="file" name="f" ><br>
		<input type="submit"  value="提交" >
	</form>
</body>

编写Servlet

Upload1.java

@WebServlet("/upload1")
public class Upload1 extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		ServletInputStream is = request.getInputStream();
		byte[] b = new byte[1024];
		int len = -1;
		while ((len = is.read(b)) != -1) {
			System.out.println(new String(b, 0, len));
		}
		is.close();
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}
}

运行效果:

在这里插入图片描述

工具类进行文件的上传

  • 在上面我们只是取到了文件的信息,此时我们里成功已经不远了,前面说过,文件的上传本质就是文件之间的copy

  • 接下来我们将使用工具类实现文件的上传操作

  • 在实际开发中,不需要我们进行数据解析,完成文件上传。因为我们会使用文件上传的工具,它们已经封装好的,提供API,只要调用它们的API就可以完成文件上传操作.

    我们使用的commons-fileupload,它是apache提供的一套开源免费的文件上传工具。

  • 使用commons-fileupload

  • 步骤

  1. 导入jar包

    commons-fileupload-1.2.1.jar 文件上传
    commons-io-1.4.jar 它是提供的io工具.
    介绍commons-fileupload
    它有三个核心
    1.DiskFileItemFactory类
    2.ServletFileUpload类
    3.FileItem

  2. 快速入门:

  3. 创建upload2.jsp页面

    <body>
    	<form action="${pageContext.request.contextPath}/upload2" method="post" encType="multipart/form-data">
    		普通文本框:<input type="text" name="content"><br>
    		文件上传:<input type="file" name="f" ><br>
    		<input type="submit"  value="提交" >
    	</form>	
    </body>
    
  4. 创建Upload2

    1.创建 DiskFileItemFactory

        DiskFileItemFactory factory = new DiskFileItemFactory();
    

    2.创建ServletFileUpload类

        ServletFileUpload upload=new ServletFileUpload(factory);		
    

    3.解析所有上传数据

    	List<FileItem> items = upload.parseRequest(request);
    
  5. 遍历items集合,集合中的每一项,就是一个上传数据.

  6. 编写代码:

    upload3.jsp

    <body>
    	<form action="${pageContext.request.contextPath}/upload3" method="post" encType="multipart/form-data">
    		普通文本框:<input type="text" name="content"><br>
    		文件上传:<input type="file" name="f" ><br>
    		<input type="submit"  value="提交" >
    	</form>	
    </body>
    

    Upload3.java

    @WebServlet("/upload3")
    public class Upload3 extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    
    	protected void doGet(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    		ServletRequestContext context = new ServletRequestContext(request);
    
    		// 1.创建 DiskFileItemFactory
    		// DiskFileItemFactory factory = new DiskFileItemFactory();//使用默认的.
    		File file = new File(this.getServletContext().getRealPath("/temp"));// 获取temp目录部署到tomcat后的绝对磁盘路径
    		DiskFileItemFactory factory = new DiskFileItemFactory(1024 * 1024, file);// 使用默认的.
    		ServletFileUpload upload = new ServletFileUpload(factory);
    		boolean flag = upload.isMultipartContent(context); // 用于判断是否是上传操作
    		if (flag) {
    			// 解决上传文件名称中文乱码
    			upload.setHeaderEncoding("utf-8");
    			// 设置上传文件大小
    			// upload.setSizeMax(1024 * 1024 * 10);// 总大小为10m
    			try {
    				List<FileItem> items = upload.parseRequest(context);
    				// 3.得到所有上传项
    				for (FileItem item : items) {
    					if (item.isFormField()) {
    						// 非上传组件
    						System.out.println("组件名称:" + item.getFieldName());
    						System.out.println("内容:" + item.getString("utf-8")); // 解决内容乱码问题
    					} else {
    						// 上传组件
    						System.out.println("组件名称:" + item.getFieldName());
    						System.out.println("上传文件名称:" + item.getName());
    
    						String name = item.getName(); // 上传文件名称
    						System.out.println(name);
    						name = name.substring(name.lastIndexOf("\\") + 1);
    						IOUtils.copy(item.getInputStream(), new FileOutputStream("f:/upload/" + name));
    						// 工具类已经帮助我们把流进行关闭
    						// 删除临时文件
    						item.delete();
    					}
    				}
    			} catch (FileUploadException e) {
    //				e.printStackTrace();
    				response.getWriter().write(e.getMessage());
    				return;
    			}
    		} else {
    			response.setContentType("text/html;charset=utf-8");
    			response.getWriter().write("不是上传操作");
    			return;
    		}
    
    	}
    
    	protected void doPost(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    		doGet(request, response);
    	}
    }
    

核心API介绍

  • DiskFileItemFactory

    • 作用:可以设置缓存大小以及临时文件保存位置.

    • 默认缓存大小是 10240(10k).

    • 临时文件默认存储在系统的临时文件目录下.(可以在环境变量中查看)

    • new DiskFileItemFactory();
      缓存大小与临时文件存储位置使用默认的.

    • DiskFileItemFactory(int sizeThreshold, File repository)

      ​ sizeThreshold :缓存大小
      ​ repository:临时文件存储位置

    • 对于无参数构造,也可以设置缓存大小以及临时文件存储位置.
      setSizeThreshold(int sizeThreshold)
      setRepository(File repository)

  • ServletFileUpload

    • ServletFileUpload upload=new ServletFileUpload(factory);
      创建一个上传工具,指定使用缓存区与临时文件存储位置.

    • List items=upload.parseRequest(request);
      它是用于解析request对象,得到所有上传项.每一个FileItem就相当于一个上传项.

    • boolean flag=upload.isMultipartContent(request);

      ​ 用于判断是否是上传.
      ​ 可以简单理解,就是判断encType=“multipart/form-data”;

    • 设置上传文件大小

      ​ void setFileSizeMax(long fileSizeMax) 设置单个文件上传大小
      ​ void setSizeMax(long sizeMax) 设置总文件上传大小

    • 解决上传文件中文名称乱码

      setHeaderEncoding(“utf-8”);

      注意:如果使用reqeust.setCharacterEncoding(“utf-8”)也可以,但不建议使用。

  • FileItem

    • isFormField

      ​ 用于判断是否是上传组件.
      ​ 如果是返回的就是false,否则返回true.

    • getFieldName();

      ​ 返回值String,得到组件名称 name属性的值

    • getName();

      返回值是String,得到的是上传文件的名称.
      注意:浏览器不同,它们得到的效果不一样。
      1.包含全路径名称 例如: C:\Users\Administrator\Desktop\a.txt
      2.只包含上传文件名称 例如:a.txt

    • getString();

      这个方法可以获取非上传组件的内容,相当于 getParameter方法作用。
      问题:如果信息是中文,会出现乱码,解决方案 getString(“utf-8”);

      ​ 如果是上传组件,上传的文件是文本文件,可以获取到文件文件的内容。
      ​ 但是如果不是文件文件,例如:是一张图片,这样获取合适吗?

    • item.getInputStream();

      ​ 获取上传文件的内容,保存到服务器端.

      ​ 它是用于读取上传文件内容的输入流.
      ​ 使用文件复制操作就可以完成文件上传。

    • item.delete();

      删除临时文件

总结:关于文件上传时的乱码问题:
		1.上传文件名称乱码
			ServletFileUpload.setHeaderEncoding("utf-8");				
		2.非上传组件内容乱码
			FileItem.getString("utf-8");
		3.思考:上传文件信息是否会乱码,需要解决吗?
			不需要解决,因为我们在上传时,使用的字节流来进行复制。

多文件上传

  • 上传文件在服务器端保存位置问题

    • 保存在可以被浏览器直接访问的位置

      ​ 例如:商城的商品图片
      ​ 保存在工程的WebRoot下的路径(不包含META-INF以及WEB-INF目录及其子目录)

    • 上传文件在服务器端保存位置问题

      ​ 例如:付费的视频

      ​ 1.工程中 META-INF WEB-INF目录及其子目录
      ​ 2.不在工程中的服务器的磁盘目录下.

  • 上传文件在同一个目录重名问题

    • 在开发中解决这个问题,可以给上传文件起随机名称。

      ​ 1.使用毫秒值
      ​ 2.使用uuid

  • 同一目录下文件过多

    只需要分目录就可以.

    ​ 1) 按照上传时间进行目录分离 (周、月 )
    ​ 2) 按照上传用户进行目录分离 ----- 为每个用户建立单独目录
    ​ 3) 按照固定数量进行目录分离 ------ 假设每个目录只能存放3000个文件 ,每当一个目录存满3000个文件后,创建一个新的目录
    ​ 4)按照文件名的hashcode进行目录分离.

对同一目录下文件过多目录分离算法的讲解

根据文件名获取文件的hashCode值,并将其hashCode值转化为16进制,然后拆分成每一个,那么每一个都作为一个目录,一共可以都可以得到16个目录。每一层有可以有8个,总的就是16的8次方足够使用

在这里我们只创建2层,即16*16一共有256个目录。

public static String getRandomDirectory(String filename) {
		//第一种写法
//		// 获取文件名的hashCode值
//		int hashcode = filename.hashCode();// 056d9363
//		// 将其hashCode转化为16进制
//		String hex = Integer.toHexString(hashcode);
//
//		return "/" + hex.charAt(0) + "/" + hex.charAt(1);
//------------------------------------------------------------
		//第二种写法
		int hashcode = filename.hashCode();
		// System.out.println(Integer.toBinaryString(hashcode));
		Integer.toBinaryString(hashcode);
		int a = hashcode & 0xf;
		hashcode = hashcode >>> 4;
		int b = hashcode & 0xf;
		return "/" + a + "/" + b;
	}
  • 每次我们使用item.getName();获取的文件路径都是一个类似:C:\Users\Administrator\Desktop\a.txt为了我们使用只得到a.txt,我们将方法抽取出来

  • 为了解决目录重名问题我们在上面获取到的真是文件名,将其改装成随机的文件名,并抽取出方法

  • 抽取目录分离算法

FileLoadUtils.java

package com.syj.Utils;
import java.util.UUID;
public class FileLoadUtils {
	// 得到上传文件真实名称 c:\a.txt 或者 a.txt
	public static String getRealName(String name) {
		// 从字符串后面数得到最后一个\的加1位置
		int len = name.lastIndexOf("\\") + 1;
		// System.out.println(len);
		return name.substring(len);
	}

	// 获取随机名称 a.txt
	public static String getUUIDFileName(String filename) {
		int len = filename.lastIndexOf(".");
		if (len != -1) {
			return UUID.randomUUID() + filename.substring(len);
		} else {
			// 有的文件没有后缀名,我们直接返回随机名称即可
			return UUID.randomUUID().toString();
		}
	}

	// 目录分离算法
	public static String getRandomDirectory(String filename) {
//		// 获取文件名的hashCode值
//		int hashcode = filename.hashCode();// 056d9363
//		// 将其hashCode转化为16进制
//		String hex = Integer.toHexString(hashcode);
//
//		return "/" + hex.charAt(0) + "/" + hex.charAt(1);
//------------------------------------------------------------------
		int hashcode = filename.hashCode();
		// System.out.println(Integer.toBinaryString(hashcode));
		Integer.toBinaryString(hashcode);
		int a = hashcode & 0xf;

		hashcode = hashcode >>> 4;

		int b = hashcode & 0xf;

		return "/" + a + "/" + b;
	}
//用来测试方法
	public static void main(String[] args) {
//		String path = getRandomDirectory("a.txt");
//		File file = new File("F:/upload");
//		File directory = new File(file, path);// 在父目录下面创建子目录
//		if (!directory.exists()) {
//			directory.mkdirs();
//		}
		getRealName("c:\\a.txt");
		getRealName("a.txt");
	}
}

实现完整的文件上传案例:

upload4.jsp使用js不断的添加文件上传的组件实现对文件上传

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>多文件上传</title>
<!-- js对文件上传 -->
<script type="text/javascript">
	function addFile() {
		var div = document.getElementById("content");
		div.innerHTML += "<div><input type='file' name='f'><input type='button' value='remove file' 'removeFile(this)'></div>";
	}
	 function removeFile(btn){
	    	
	    	document.getElementById("content").removeChild(btn.parentNode);
	  }
</script>
</head>
<body>
	<input type="button" value="add File" onclick="addFile();" />

	<form action="${pageContext.request.contextPath}/upload4" method="post"	encType="multipart/form-data">
		普通文本框:<input type="text" name="content"><br> 文件上传:<input
			type="file" name="f"><br>
		<div id="content"></div>
		<input type="submit" value="提交">
	</form>
</body>
</html>

Upload4.java核心代码完整的文件上传

package com.syj.Web.Servlet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
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.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.apache.tomcat.util.http.fileupload.servlet.ServletRequestContext;
import com.syj.Utils.FileLoadUtils;

@WebServlet("/upload4")
public class Upload4 extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		ServletRequestContext context = new ServletRequestContext(request);
		response.setContentType("text/html;charset=utf-8");
		// 1.创建 DiskFileItemFactory
		// DiskFileItemFactory factory = new DiskFileItemFactory();//使用默认的.
		File file = new File(this.getServletContext().getRealPath("/temp"));// 获取temp目录部署到tomcat后的绝对磁盘路径
		DiskFileItemFactory factory = new DiskFileItemFactory(1024 * 1024, file);// 使用默认的.
		// 2.创建ServletFileUpload
		ServletFileUpload upload = new ServletFileUpload(factory);
		boolean flag = upload.isMultipartContent(context); // 用于判断是否是上传操作
		if (flag) {
			// 解决上传文件名称中文乱码
			upload.setHeaderEncoding("utf-8");
			// 设置上传文件大小
			// upload.setSizeMax(1024 * 1024 * 10);// 总大小为10m
			try {
				List<FileItem> items = upload.parseRequest(context);
				// 3.得到所有上传项
				for (FileItem item : items) {
					if (!item.isFormField()) {
						// 上传文件名称
						String name = item.getName();

						// 得到上传文件的真实名称
						String filename = FileLoadUtils.getRealName(name);

						// 给文件一个随机名称
						String uuidname = FileLoadUtils.getUUIDFileName(filename);

						// 得到随机目录
						String randomDirectory = FileLoadUtils.getRandomDirectory(filename);

						// 1.假如上传到本地,比如F:/upload
						// File rd = new File("F:/upload", randomDirectory);
						// 1.假如到项目目录下,可访问
						String parentPath = this.getServletContext().getRealPath("/upload");
						File rd = new File(parentPath, randomDirectory);

						// 注意随机目录可能不存在需要自己创建
						if (!rd.exists()) {
							rd.mkdirs();
						}

						IOUtils.copy(item.getInputStream(), new FileOutputStream(new File(rd, uuidname)));
						// 工具类已经帮助我们把流进行关闭
						// 删除临时文件
						item.delete();
					}
				}
			} catch (FileUploadException e) {
//				e.printStackTrace();
				response.getWriter().write(e.getMessage());
				return;
			}
		} else {
			response.getWriter().write("不是上传操作");
			return;
		}
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}
}

发现的问题:

  • 发现原来的parseRequest(reqeust)方法已经不建议使用了,需要换成RequestContext才行
  • 代码中推荐使用RequestContext

在这里插入图片描述

  • 原来的parseRequest(HttpServletRequest req)已经不推荐使用
  • 那么RequestContext好像没有见到过,又该如何获取?我们应该养成自学的习惯,有一种思维

解决问题:

  • 发现RequestContext是一个接口。

在这里插入图片描述

  • 我们可以查看他的实现接口和实现类

在这里插入图片描述

  • 分别查看:

在这里插入图片描述

在这里插入图片描述

  • 对于ServletRequestContext我们比较熟悉

在这里插入图片描述

  • 至此问题基本得到解决

    体现在代码中:

    ServletRequestContext context = new ServletRequestContext(request);//第26行
    List<FileItem> items = upload.parseRequest(context);//第41行
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值