文件的上传与下载

[TOC]

文件上传技术

  • Servlet3.0
  • JSPSmartUpload:嵌入到JSP中完成文件上传.主要用于Model1年代的.
  • FileUpload:Apache的文件上传组件.
  • Struts2:底层是FileUpload.

文件上传的要素

  • 表单的提交的方式必须是POST.
  • 表单中需要有文件上传的表单元素:这个元素这个元素必须有name属性和值<input type=”file” name=”upload”>
  • 表单的enctype属性的值必须是multipart/form-data.

一、文件上传原理分析

没有设置enctype属性的时候:只能获得文件的名称,而没有文件内容,如下图

设置enctype属性为multipart/form-data:获得到文件名及文件内容,如下图
文件上传的原理图

二、上传方式

使用Servlet3.0实现文件上传

代码如下

<body>
<h1>文件上传的页面</h1>
<!-- 
	* 表单的提交的方式必须是POST.
	* 表单中需要有文件上传的表单元素:这个元素这个元素必须有name属性和值:<input type=”file” name=”upload”>
	* 表单的enctype属性的值必须是multipart/form-data.
 -->
<form action="${ pageContext.request.contextPath }/UploadServlet" method="post" enctype="multipart/form-data">
	<table border="1" width="600">
		<tr>
			<td>文件描述</td>
			<td><input type="text" name="filedesc"></td>
		</tr>
		<tr>
			<td>文件上传</td>
			<td><input type="file" name="upload"></td>
		</tr>
		<tr>
			<td colspan="2"><input type="submit" value="上传"></td>
		</tr>
	</table>
</form>
</body>
复制代码
/**
 * 文件上传的Servlet
 */
@WebServlet("/UploadServlet")
@MultipartConfig
public class UploadServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 接收普通数据:
		request.setCharacterEncoding("UTF-8");
		String filedesc = request.getParameter("filedesc");
		System.out.println("文件描述"+filedesc);
		// 接收文件:
		Part part = request.getPart("upload");
		
		long size = part.getSize();// 获得文件大小:
		System.out.println("文件大小:"+size);
		String name = part.getName();
		System.out.println("文件表单中的name属性的名称"+name);
		// 获得文件名:
		String header = part.getHeader("Content-Disposition");
		int idx = header.lastIndexOf("filename=\"");
		String fileName = header.substring(idx+10, header.length()-1);
		//IE浏览器不行,加上以下代码
		//String[] split = fileName.split("//");
		//fileName = split[split.length-1];
		System.out.println("文件名:"+fileName);
		// 获得文件内容:
		InputStream is = part.getInputStream();
		// 获得upload的路径:
		//项目根路径下不安全,最好存到web-inf目录下
		String path = this.getServletContext().getRealPath("/WEB-INF/upload");
		// 获得文件的唯一文件名:
		String uuidFileName = UUIDUtils.getUUIDFileName(fileName);
		String realPath = path+UploadUtils.getPath(uuidFileName);
		File file = new File(realPath);
		if(!file.exists()){
			file.mkdirs();
		}
		OutputStream os = new FileOutputStream(realPath+"/"+uuidFileName);
		byte[] b = new byte[1024];
		int len = 0;
		while((len = is.read(b))!=-1){
			os.write(b, 0, len);
		}
		is.close();
		os.close();
	}
复制代码

使用fileUpload实现文件上传

/*	
	 文件的上传:
	     * 三个要素:
	         * 提交的方式是POST:
	         * 表单中需要有<input type=”file” name=”upload”>
	         * enctype=”multipart/form-data”设置后request.getParamater()就不能用了。

	     * 文件上传的技术:
	         * Servlet3.0
	         * JSPSmartUpload
	         * FileUpload:			上传文件大小有限制
	             * commons-fileupload-1.2.1.jar
	             * commons-io-1.4.jar
	         * Struts2

	     * 使用FileUpload的时候:
	         * 获得磁盘文件工厂对象:
	         * 通过工厂获得核心解析类:
	         * 解析request对象 , 返回集合,集合中的内容是分割线分成的每个部分.
	         * 遍历每个部分:
*/	        	 
	      

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		try {
			// 创建一个Product的对象:
			Product product = new Product();
			// 创建磁盘文件项工厂.
			DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
			// 设置缓存区的大小: 如果文件的大小超过了缓冲区的大小,就会产生临时文件.
			diskFileItemFactory.setSizeThreshold(3 * 1024 * 1024);
			// 设置临时文件存放的路径:
			// diskFileItemFactory.setRepository(repository);
			// 获得核心解析类:ServletFileUpload
			ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);
			fileUpload.setHeaderEncoding("UTF-8");// 解决中文文件名上传乱码.
			// fileUpload.setFileSizeMax(fileSizeMax); // 设置单个文件大小
			// fileUpload.setSizeMax(sizeMax); // 设置表单中的所有文件项的文件总大小
			// 解析request 返回List集合
			List<FileItem> list = fileUpload.parseRequest(request);
			// 获得每个部分:
			// 将遍历的值存入到一个Map集合中:
			Map<String,String> map = new HashMap<String,String>();
			String fileName=null;
			for (FileItem fileItem : list) {
				//&emsp;判断普通项和文件上传项:
				if(fileItem.isFormField()){
					// 普通项
					String name = fileItem.getFieldName();
					String value = fileItem.getString("UTF-8"); // 解决的是普通项的中文乱码.
					System.out.println(name+"   "+value);
					map.put(name, value);
				}else{
					// 文件上传项 
					// 获得文件名:
					fileName = fileItem.getName();
					System.out.println("文件名:"+fileName);
					// 获得文件的输入流:
					InputStream is = fileItem.getInputStream();
					// 获得文件要上传的路径:
					String path = this.getServletContext().getRealPath("/products/1");
					OutputStream os = new FileOutputStream(path+"/"+fileName);
					int length = 0;
					byte[] b= new byte[1024];
					while((length = is.read(b))!=-1){
						os.write(b, 0, length);
					}
					is.close();
					os.close();
				}
			}
			// 封装数据:
			BeanUtils.populate(product, map);
			product.setPid(UUIDUtils.getUUID());
			product.setPdate(new Date());
			product.setPflag(0);
			product.setPimage("products/1/"+fileName);
			Category category = new Category();
			category.setCid(map.get("cid"));
			product.setCategory(category);
			
			// 存入到数据库:
			ProductService productService = (ProductService) BeanFactory.getBean("productService");
			productService.save(product);
			
			// 页面跳转:
			response.sendRedirect(request.getContextPath()+"/AdminProductServlet?method=findByPage&currPage=1");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
复制代码

Struts2文件上传

浏览器端注意事项

表单提交方式method=post 表单中必须有一个组件 表单中必须设置enctype=”multipart/form-data”

服务器端

Commons-fileupoad.jar包完成。

Struts2框架使用一个fileupload的interceptor来完成文件上传

上传案例

<form action="${ pageContext.request.contextPath }/upMany" enctype="multipart/form-data" method="post">
<input type="file" name="upload">
<input type="file" name="upload">
<input type="submit" value="上传">
</form>
复制代码
public class UploadActionMany extends ActionSupport{
//	 extends ActionSupport 
	private File[] upload;
	private String[] uploadContentType;
	private String[] uploadFileName;
/*
	提供对应的set/get方法
*/
	public String upload(){
		System.out.println(3);
		String path = ServletActionContext.getServletContext().getRealPath("/upload");
		try {
			for (int i = 0; i < upload.length; i++) {
				File file = new File(path,uploadFileName[i]);
				FileUtils.copyFile(upload[i], file);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println(4);
		return null;
	}
}
复制代码

注意事项

<!-- 开发模式 -->
<constant name="struts.devMode" value="true"></constant>
<!-- 设置文件上传的大小限制 -->
<constant name="struts.multipart.maxSize" value="40971520"></constant>
<action name="upMany" class="com.lbb.struts2.action.UploadActionMany" method="upload">
	<!-- 文件上传出错后的视图 -->
	<result name="input">/error.jsp</result>
	<interceptor-ref name="fileUpload">
		<!-- <param name="maximumSize"></param> --><!-- 设置每一个文件的单独的上传大小 -->
		<!-- <param name="allowedTypes"></param> --><!-- 文件的mime类型 -->
		<param name="allowedExtensions">txt,jpg,bmp</param><!-- 设置允许的后缀名 -->
	</interceptor-ref>
	<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
复制代码

input接受错误信息

<s:actionerror/>
<s:fielderror/>
复制代码

三、文件上传问题

文件重名

使用UUID生成随机的唯一文件名

同一目录下文件过多

一个目录下文件过多,导致打开都很慢,更别说是读写操作. 目录分离方案

  • 按时间分:一个月一个目录,一个星期一个目录,一天一个目录
  • 按数量分:一个目录下存5000个文件,创建一个新的目录,再去存放
  • 按用户分:为每个用户创建一个单独目录 存放文件
  • 按目录分离算法分 使用唯一文件名.hashCode(); -- 得到一个代表当前这个文件的int类型值. int类型占4个字节32位.可以让hashCode值&0xf; 得到一个int值,用这个int值作为一级目录. 让hashCode右移4位 &0xf ;得到一个int值,用这个int值作为二级目录.依次类推.
public class UploadUtils {

	public static String getPath(String uuidFileName){
		// 使用唯一文件名.hashCode();
		int code1 = uuidFileName.hashCode();
		int d1 = code1 & 0xf; // 获得到1级目录.
		int code2 = code1 >>> 4;
		int d2 = code2 & 0xf; // 获得到2级目录.
		return "/"+d1+"/"+d2;
	}
}
复制代码

原理图如下

四、文件下载

文件下载的方式

一种:超链接下载.直接将文件的路径写到超链接的href中.---前提:文件类型,浏览器不支持. 二种:手动编写代码的方式完成文件的下载. 设置两个头和一个流: Content-Type:文件的MIME的类型. Content-Disposition :以下载的形式打开文件. InputStream:文件的输入流.

<body>
<h1>文件下载的列表页面</h1>
<h3>超链接的下载</h3>
<a href="/day10/download/hello.txt">hello.txt</a><br/>
<a href="/day10/download/cs10001.jpg">cs10001.jpg</a><br/>
<a href="/day10/download/hello.zip">hello.zip</a><br/>

<h3>手动编码方式下载</h3>
<a href="/day10/DownloadServlet?filename=hello.txt">hello.txt</a><br/>
<a href="/day10/DownloadServlet?filename=cs10001.jpg">cs10001.jpg</a><br/>
<a href="/day10/DownloadServlet?filename=hello.zip">hello.zip</a><br/>
<a href="/day10/DownloadServlet?filename=美女.jpg">美女.jpg</a><br/>
</body>
复制代码
IE浏览器下载中文文件的时候采用的URL的编码.
Firefox浏览器下载中文文件的时候采用的是Base64的编码.
/**
 * 文件下载的Servlet
 */
public class DownloadServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 1.接收参数
		String filename = new String(request.getParameter("filename").getBytes("ISO-8859-1"),"UTF-8");
		System.out.println(filename);
		// 2.完成文件下载:
		// 2.1设置Content-Type头
		String type = this.getServletContext().getMimeType(filename);
		response.setHeader("Content-Type", type);
		// 2.3设置文件的InputStream.
		String realPath = this.getServletContext().getRealPath("/download/"+filename);
		
		// 根据浏览器的类型处理中文文件的乱码问题:
		String agent = request.getHeader("User-Agent");
		System.out.println(agent);
		if(agent.contains("Firefox")){
			filename = base64EncodeFileName(filename);
		}else{
			filename = URLEncoder.encode(filename,"UTF-8");
		}
		
		// 2.2设置Content-Disposition头
		response.setHeader("Content-Disposition", "attachment;filename="+filename);
		
		InputStream is = new FileInputStream(realPath);
		// 获得response的输出流:
		OutputStream os = response.getOutputStream();
		int len = 0;
		byte[] b = new byte[1024];
		while((len = is.read(b))!= -1){
			os.write(b, 0, len);
		}
		is.close();
	}
	
	public static String base64EncodeFileName(String fileName) {
		BASE64Encoder base64Encoder = new BASE64Encoder();
		try {
			return "=?UTF-8?B?"
					+ new String(base64Encoder.encode(fileName
							.getBytes("UTF-8"))) + "?=";
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

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

转载于:https://juejin.im/post/5acb7ef76fb9a028be364c98

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值