文件上传与下载(免去重学烦恼)


一、文件上传

文件上传:将本地的文件通过流写入到服务器的过程。

文件上传的三个要素
①表单的提交的方式需要是POST
②表单中需要有<input type=“file”>元素,需要有name属性和值。
③表单enctype=“multipart/form-data”

原理分析 :手动实现文件上传
1.获得分割线
2.获得请求体的所有内容request.getInputstream()
3.利用分割线将获得内容进行分割。
4.判断是普通项还是文件上传项
普通项:获得普通项名称和值
文件上传项:获得文件名称和文件内容
5.通过流写到服务器上。

1.入门案例

UploadServlet

package com.upload.demo;

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.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;



public class UploadServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		try {
			DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
			ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);
			List<FileItem> list = fileUpload.parseRequest(request);
			for (FileItem fileItem : list) {
				if (fileItem.isFormField()) {
					//普通
					String name = fileItem.getFieldName();//获得普通项名称
					String value = fileItem.getString("UTF-8");
					System.out.println(name+"    "+value);
				}else {
					String filename = fileItem.getName();//获得文件名称
					InputStream is = fileItem.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);
					}
					is.close();
					os.close();
				}
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
	}

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

}

upload.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>文件上传</h1>
<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>
</html>

运行后可以上传文件,本地且可以接受,控制台显示文件信息

2.API方法介绍

DiskFileItemFactory : 磁盘文件项工厂

构造方法:
DiskFileltemFactory()

DiskFileltemFactory(int sizeThreshold,File repostory)
sizeThreshold : 设置文件上传的缓冲区的大小,默认值为10kb。
repostory : 设置文件上传过程中产生临时文件存放的路径。

方法:
setSizeThreshold() : 设置缓冲区的大小
setRepository() : 设置临时文件存放的路径

基本和入门代码一样,添加了这两个方法的使用:

package com.upload.demo;

import java.io.File;
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.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;



public class UploadServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		try {
			//1.创建磁盘文件项工厂
			DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
			//1.1设置缓冲区大小
			diskFileItemFactory.setSizeThreshold(3*1024*1024);//缓冲区大小为3M
			//1.2设置临时文件存放路径:
			//获得临时文件存放路径
			String tempPath = getServletContext().getRealPath("/temp");
			diskFileItemFactory.setRepository(new File(tempPath));
			//2.创建核心解析类
			ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);
			
			List<FileItem> list = fileUpload.parseRequest(request);
			for (FileItem fileItem : list) {
				if (fileItem.isFormField()) {
					//普通
					String name = fileItem.getFieldName();//获得普通项名称
					String value = fileItem.getString("UTF-8");
					System.out.println(name+"    "+value);
				}else {
					String filename = fileItem.getName();//获得文件名称
					InputStream is = fileItem.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);
					}
					is.close();
					os.close();
				}
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
	}

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

}

ServletFileUpload : 核心解析类

构造方法:
serveltFileUpload(FileltemFactory fileltemFactory);

方法:
ServletFileUpload.isMultipartContent(request);
是用来判断表单的enctype属性是否正确

List<FileItem> list = fileUpload.parseRequest(request);
解析Request 对象,返回一个List集合(每个部分的对象Fileltem)

fileUpload.setFileSizeMax(fileSizeMax);
设置单个文件的大小

fileUpload.setSizeMax(sizeMax);
设置上传的文件的总大小

fileUpload.setHeaderEncoding(encoding);
解决中文乱码的问题,如果中文乱码就使用

fileUpload.setProgressListener(pListener);
监听文件上传速度

FileItem : 文件项

方法:
fileItem.isFormField()
判断表单项是普通项还是文件上传项。如果为true代表是普通项

普通项方法:
fileItem.getFieldName();
获得普通项名称

fileItem.getString();
fileItem.getString(“UTF-8”); //如果乱码则使用这个来获得值
获得普通项值

文件上传项:
fileItem.getName();
获得文件上传的文件名的方法

fileItem.getInputStream();
获得文件内容

fileItem.getSize()
获得文件上传的文件大小

fileItem.delete();
删除文件上传过程中的临时文件

代码附上注释:

package com.upload.demo;

import java.io.File;
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.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;



public class UploadServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		try {
			boolean flag = ServletFileUpload.isMultipartContent(request);
			if(!flag) {
				request.setAttribute("msg", "表单的格式不正确");
				request.getRequestDispatcher("/day03/upload.jsp").forward(request, response);
				return;
			}
			
			//1.创建磁盘文件项工厂
			DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
			//1.1设置缓冲区大小
			diskFileItemFactory.setSizeThreshold(3*1024*1024);//缓冲区大小为3M
			//1.2设置临时文件存放路径:
			//获得临时文件存放路径
			String tempPath = getServletContext().getRealPath("/temp");
			diskFileItemFactory.setRepository(new File(tempPath));
			//2.创建核心解析类
			ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);
			//设置文件上传的文件大小
			fileUpload.setSizeMax(5*1024*1024);

			//3.利用核心解析类解析Request,解析后会得到多个部分。返回一个List集合。
			//List集合装的是每个部分的内容(FileItem文件项)
			List<FileItem> list = fileUpload.parseRequest(request);
			//4.遍历List集合,会得到代表每个部分的文件项的对象。根据文件项判断是否是文件上传项
			for (FileItem fileItem : list) {
				//判断这个文件项是否是普通项还是文件上传项
				if (fileItem.isFormField()) {
					//普通项:
					//接受普通项的值:(接收值不能使用request.getParameter)
					String name = fileItem.getFieldName();//获得普通项名称
					String value = fileItem.getString("UTF-8");
					System.out.println(name+"    "+value);
				}else {
					//文件上传项:
					//获得文件上传项文件名称和数据
					String filename = fileItem.getName();//获得文件名称
					InputStream is = fileItem.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);
					}
					is.close();
					os.close();
				}
				//删除临时文件
				fileItem.delete();
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
	}

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

}

3.文件上传应用和问题的解决

js控制多文件上传

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
	function add(){
		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="上传"/><br/>
	<div id="div1">
	
	</div>
</form>

</body>
</html>

浏览页面如图:可以通过添加按钮实现多文件上传
在这里插入图片描述

文件上传兼容浏览器问题

如果使用IE老版本的浏览器出现一个文件名称获取错误问题。IE老版本获取文件名称的时都会带有路径。
解决方法如下:在获取文件名时候,检查一下是否有\然后截取即可

String filename = fileItem.getName();//获得文件名称
int idx = filename.lastIndexOf("\\");
if (idx!=-1) {
	//使用的是老版本的浏览器
filename = filename.substring(idx+1);
}

文件上传同一个目录下文件同名的问题

张三向服务器上传了一个文件 aa.txt内容是hello world。李四向服务器上传了一个文件aa.txt 内容 hello Java。后上传的文件将先上传的文件覆盖了。

使用唯一文件名进行解决。
创建一个工具类:生成随机字符串

package com.upload.demo;

import java.util.UUID;

public class UploadUtils {
	public static String getUuidFilename(String filename) {
		//在java的API中有一个类UUID可以产生随机的字符串
		UUID.randomUUID().toString();
		
		int idx = filename.lastIndexOf(".");
		String extetions = filename.substring(idx);
		
		return UUID.randomUUID().toString().replace("-", "")+extetions;
	}
}

在servlet中使用

//获得唯一文件名
String uuidFilename = UploadUtils.getUuidFilename(filename);
InputStream is = fileItem.getInputStream();
//获得文件上传写入路径,绝对路径
String realPath = getServletContext().getRealPath("/upload");
//创建输出流,写入到设置的路径中
OutputStream os = new FileOutputStream(realPath+"/"+uuidFilename);//这里也要修改。

文件上传同个目录下文件过多的问题

现在所有的用户都上传文件,如果网站访问量比较大,如果都上传到同一个目录下,在同一个目录下存放的文件太多了,也会对程序有影响〈其实打开该目录的时候,都会很卡,更别说读写操作)。

目录分离 :
按时间分离 : 按月、周、天、小时
按用户分离 : 按张三、李四。
按个数分离 : 一个目录下存放3000个文件。
按目录分离算法 : 按照某种特定算法进行分离。

分离算法举例:
-----》例如:上传一个文件,得到一个唯一的文件名。唯一文件名获取其hashcode值。
-----》int类型的值(32位)。让hashCode的值& 0xf;
-----》得出的这个值作为一级目录。让hashcode右移4位& 0xf;
-----》得出的这个值作为二级目录。
以此类推。
在这里插入图片描述

//获得唯一文件名
String uuidFilename = UploadUtils.getUuidFilename(filename);
InputStream is = fileItem.getInputStream();
//获得文件上传写入路径,绝对路径
String realPath = getServletContext().getRealPath("/upload");
//进行目录分离
String path = UploadUtils.getRealPath(uuidFilename);
String newPath = realPath + path;
File file = new File(newPath);
if(!file.exists()) {
	file.mkdirs();
}
//创建输出流,写入到设置的路径中
OutputStream os = new FileOutputStream(newPath+"/"+uuidFilename);

二、文件下载

文件下载:将服务器上的一个文件,通过流写入到客户端上。

①使用超链接的方式实现文件的下载。
<a href=“文件的路径”>超链接</a>
注意:超链接的方式,如果浏览器不能识别这种格式的文件,提示下载。如果支持该格式的文件,直接打开。
②通过手动编写代码的方式实现文件的下载
设置两个头和一个流
content-Type : 文件的MIME的类型
Content-Disposition : 浏览器支持该格式的文件,提示下载
设置代表该文件的输入流 , 渝出流是固定response.getOutputStream()

1.手动编码方式下载

代码演示:下载资源放在day03的文件夹中
在这里插入图片描述

downloadServlet

package com.download.demo;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class DownloadServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//1.接受参数
		String filename = request.getParameter("filename");
		//2.下载:设置两个头和一个流
		//设置Content-Type
		String type = getServletContext().getMimeType(filename);
		response.setContentType(type);
		//设置Content-Disposition
		response.setHeader("Content-Disposition", "attachment;filename="+filename);
		//设置代表文件的输入流
		String path = getServletContext().getRealPath("/day03");
		InputStream is = new FileInputStream(path+"/"+filename);
		OutputStream os = response.getOutputStream(); 
		byte[] b = new byte[1024];
		int len = 0;
		while ((len=is.read(b))!=-1) {
			os.write(b,0,len);
		}
		is.close();
	}

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

}

download.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>文件下载:手动编码方式</h1>
<h3><a href="${ pageContext.request.contextPath }/DownloadServlet?filename=02.jpg">02.jpg</a></h3>
<h3><a href="${ pageContext.request.contextPath }/DownloadServlet?filename=a.zip">a.zip</a></h3>
</body>
</html>

运行后网站打开可运行。

中文文件下载

DownloadServlet

package com.download.demo;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class DownloadServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//1.接受参数
		String filename = new String(request.getParameter("filename").getBytes("ISO-8859-1"),"UTF-8");
		//2.下载:设置两个头和一个流
		//设置Content-Type
		String type = getServletContext().getMimeType(filename);
		response.setContentType(type);


		//修改如下:

		//定义一个代表文件的路径
		String path = getServletContext().getRealPath("/day03");
		File file = new File(path+"/"+filename);
		//判断浏览器类型
		String agent = request.getHeader("User-Agent");
		if (agent.contains("Firefox")) {
			//火狐浏览器
			filename = DownloadUtils.base64EncodeFileName(filename);
		}else {
			//IE或者其他浏览器
			filename = URLEncoder.encode(filename,"UTF-8");
		}
		//设置Content-Disposition
		response.setHeader("Content-Disposition", "attachment;filename="+filename);
		//设置代表文件的输入流
		InputStream is = new FileInputStream(file);







		OutputStream os = response.getOutputStream(); 
		byte[] b = new byte[1024];
		int len = 0;
		while ((len=is.read(b))!=-1) {
			os.write(b,0,len);
		}
		is.close();
	}

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

}

DownloadUtils

package com.download.demo;

import java.io.UnsupportedEncodingException;

import org.apache.commons.codec.binary.Base64;

public class DownloadUtils {
	public static String base64EncodeFileName(String fileName) {
		try {
			return "?UTF-8?B?"+new String(Base64.encodeBase64(fileName.getBytes("UTF-8")))
					+"?=";
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
		
	}
}

修改后就可以下载中文路径的文件

2.给定目录下的文件下载

给定一个目录(这个目录可以是任意盘符下的任意路径―这个路径下有多少级目录,每级目录中有多少个文件都是未知的)。将这个路径中的文件显示到页面上,在页面上给每个问题件都提供响应下载的链接,当点击这个链接的时候,对该文件进行下载。
downloadList.jsp

<%@page import="java.io.File"%>
<%@page import="java.util.Queue"%>
<%@page import="java.util.LinkedList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>树形遍历</h1>
<%
	Queue<File> queue = new LinkedList<File>();
	File root = new File("E:/resource");
	queue.offer(root);
	while(!queue.isEmpty()){
		File file = queue.poll();
		File[] files = file.listFiles();
		for(File f : files){
			if(f.isFile()){
%>
<h4><a href="${ pageContext.request.contextPath }/DownloadListServlet?filename=<%= f.getAbsolutePath() %>"><%= f.getName() %></a></h4>				
				
<%				
			}else{
				queue.offer(f);
			}
		}
	}
%>
</body>
</html>

DownloadListServlet

package com.download.demo;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class DownloadListServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		String path = request.getParameter("filename");
		if (path!=null) {
			path = new String(path.getBytes("ISO-8859-1"),"UTF-8");
		}
		File file = new File(path);

		//实现文件下载:设置两个头和一个流:
		//获得文件名
		String filename = file.getName();
		response.setContentType(getServletContext().getMimeType(filename));
		//设置另一个头:
		String agent = request.getHeader("User-Agent");
		if (agent.contains("Firefox")) {
			filename = DownloadUtils.base64EncodeFileName(filename);
		}else {
			filename = URLEncoder.encode(filename,"UTF-8");//如果有空格,会把他编码为+
			filename = filename.replace("+", " ");//把+替换成空格就不会有小问题
		}
		response.setHeader("Content-Disposition", "attachment;filename="+filename);
		InputStream is = new FileInputStream(file);
		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();
		
	}

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

}

注意:如果有报错400,后端无效字符解析错误。则可以去修改servlet.xml配置

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
			   URIEncoding="UTF-8" 
			   URLEncoding="UTF-8"
			   useBodyEncodingForURI="true"
			   relaxedPathChars="|{}[],%.\"
			   relaxedQueryChars="|{}[],%.\" />

原因是提交的get方式的url中有tomcat解析不了的字符,极大可能是\这个字符没有解析进去。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值