【JavaWeb】20 上传下载

这篇博客详细讲解了JavaWeb中的上传和下载操作,包括上传的限制、步骤、细节处理,以及如何处理文件名乱码、同名问题和缓存大小。同时,介绍了邮件的发送,包括使用javamail发邮件、处理附件以及邮件协议的基本知识。
摘要由CSDN通过智能技术生成

上传

上传对表单的限制:

  • method=“post”
  • enctype=“multipart/form-data”
  • 添加文件表单项<input type="file" name="xxx">
<form action="xxx" method="post" enctype="multipart/form-data">
</form>

上传对Servlet的限制:

  • request.getParameter("xxx")返回的是String,在表单为enctype="multipart/form-data"时返回值为null,因此不能使用了
  • 使用ServletInputString request.getInputStream(),它返回整个请求体的流

首先写一个页面form.jsp用于上传:

<body>
<h1>上传1</h1>
<form action="<c:url value='/Upload1Servlet'/>" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"/><br/>
照   片:<input type="file" name="photo"/><br/>
<input type="submit" value="上传"/>
</form>
</body>

Upload1Servlet

public class Upload1Servlet extends HttpServlet {
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		ServletInputStream in=request.getInputStream();
		//注意此处需要导入commmons-io.jar
		String s=IOUtils.toString(in);
		System.out.println(s);
	}
}

将上传的请求打印到控制台上,在多部件表单中,每个表单项一个部件,每个部件包含自己的请求头、空行、请求体。
普通表单项:

  • 1个头:Content-Disposition:包含name=“xxxx”,即表单项名称。
  • 体就是表单项的值

文件表单项:

  • 2个头:
    Content-Disposition:包含name=“xxxx”,即表单项名称;还有一个filename=“xxx”,表示上传文件的名称
    Content-Type:它是上传文件的MIME类型,例如:image/pjpeg,表示上传的是图片,图上中jpg扩展名的图片。
  • 体就是上传文件的内容。

因此,如果要解析图片,需要拿到图片部件的数据。这里我们可以使用commons-fileupload,它会解析request中的上传数据,并将结果封装到一个FileItem对象中,我们只需调用FileItem的方法即可。
要使用commons-fileupload需要导入两个包:
commons-fileupload.rar
commons-io.jar(依赖)

上传三步

相关类
工厂:DiskFileItemFactory
解析器:ServletFileUpload
表单项:FileItem

  1. 创建工厂:DiskFileItemFactory factory=new DiskFileItemFactory()
  2. 创建解析器:ServletFileUpload sfu=new ServletFileUpload(factory)
  3. 使用解析器解析request,得到FileItem集合:List<FileItem> fileItemList=sfu.parseRequest(request)

FileItem
boolean isFormField():是否为普通表单项
String getFiledName():返回当前表单名称
String getString(String charset):返回表单项的值
String getName():返回上传的文件名
long getSize():返回上传文件的字节数
InputStream getInputStream():返回上传文件对应的输入流
void write(File destFile):把上传的文件内容保存到指定的文件中

使用举例:
form2.jsp

<body>
<h1>上传2</h1>
<form action="<c:url value='/Upload2Servlet'/>" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"/><br/>
照   片:<input type="file" name="photo"/><br/>
<input type="submit" value="上传"/>
</form>
</body>

Upload2Servlet

public class Upload2Servlet extends HttpServlet {
	public void doPost(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		//得到工厂
		DiskFileItemFactory factory=new DiskFileItemFactory();
		//创建解析器
		ServletFileUpload sfu=new ServletFileUpload(factory);
		//得到FileItem集合
		try {
			List<FileItem> fileItemList=sfu.parseRequest(request);
			FileItem fi1=fileItemList.get(0);
			FileItem fi2=fileItemList.get(1);
			System.out.println("普通表单项演示"+fi1.getFieldName()+"="+fi1.getString("utf-8"));
			System.out.println("文件表单项演示:");
			System.out.println("Content-type:"+fi2.getContentType());
			System.out.println("Size:"+fi2.getSize());
			System.out.println("filename"+fi2.getName());
			//保存文件
			File destFile=new File("d:/desktop/test.jpg");
			try {
				fi2.write(destFile);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		} catch (FileUploadException e) {
			throw new RuntimeException(e);
		}
	}
}

上传的细节

  1. 文件必须保存到WEB-INF下,避免浏览器直接访问到(如果项目不再使用这个文件,就可以放在其他盘,项目之外)
  2. 有的浏览器上传的文件名是绝对路径,如C:\files\a.jpg,这需要切割
String filename=fi2.getName();
int index=filename.lastIndexOf("\\");
if(index!=-1)
	filename=filename.substring(index+1);
  1. 文件名乱码或文件表单项乱码:request.setCharacterEncoding(“utf-8”),因为fileupload内部会调用request.getCharacterEncoding;或者使用servletFileUpload.setHeaderEncoding("utf-8"),优先级更高
  2. 文件同名问题:为每个文件添加名称前缀(uuid),后缀不能改,例如:
filename=CommonUtils.uuid()+"_"+filename;
  1. 目录打散:不能在一个目录下存放过多文件。
    首字符打散:首字符作为目录
    时间打散:当前日期做目录
    哈希打散:
    1.通过文件名称获取int值,即调用hashCode()
    2.将int值转换为16进制
    3.将16进制前两位用来生成两层目录,如1B2C3D4E5---->1/B/
  2. 上传文件大小限制:单个文件、整个请求
  3. 缓存大小与临时目录

改进后的上传

form3.jsp

<body>
<h1>上传3</h1>
<form action="<c:url value='/Upload3Servlet'/>" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"/><br/>
照   片:<input type="file" name="photo"/><br/>
<input type="submit" value="上传"/>
</form>
</body>

Upload3Servlet:限制单个文件上传大小(必须在解析开始前调用)、限制表单总大小(必须在解析开始前调用)、解决文件名为绝对路径的问题、解决文件名重复的问题、利用哈希打散存放文件(在WEB-INF下新建files文件夹存放文件)

public class Upload3Servlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	request.setCharacterEncoding("utf-8");
	response.setContentType("text/html;charset=utf-8");
	DiskFileItemFactory factory=new DiskFileItemFactory();
	ServletFileUpload sfu=new ServletFileUpload(factory);
	sfu.setFileSizeMax(100*1024);//限制单个文件大小为100k
	sfu.setSizeMax(1024*1024);//限制整个表单大小1M
	try {
		List<FileItem> list=sfu.parseRequest(request);
		FileItem fi=list.get(1);
		//得到文件保存路径
		String root=this.getServletContext().getRealPath("/WEB-INF/files/");
		//得到文件名
		String filename=fi.getName();
		int index=filename.lastIndexOf("\\");
		if(index!=-1)
			filename=filename.substring(index+1);
		String savename=CommonUtils.uuid()+"_"+filename;
		//得到hashCode
		int hCode=filename.hashCode();
		//转换成16进制
		String hex=Integer.toHexString(hCode);
		//获取前两个字符用来生成目录
		File dirFile=new File(root,hex.charAt(0)+"/"+hex.charAt(1));
		//创建目录链
		dirFile.mkdirs();
		//创建目录文件
		File destFile=new File(dirFile,savename);
		//保存
		fi.write(destFile);
	} catch (Exception e) {
		throw new RuntimeException(e);
	}
}
}

上传文件后在tomcat的项目下可以找到传送的文件,已经用哈希打散生成的目录存放:
在这里插入图片描述

缓存大小与临时目录

在创建工厂时设置缓存大小、临时目录:超出设置的默认值会向临时目录保存(每默认值大小保存一次),在fi.write()时才从临时目录拷贝到指定目录。
如:每20kb保存一次到临时目录f:/temp中
DiskFileItemFactory factory=new DiskFileItemFactory(20*1024,new File("f:/temp"));

下载

下载就是向客户端响应字节数据。
把一个文件变成字节数组,使用response.getOutputStream()来响应给浏览器。

下载的要求:
两个头:
Content-Type:传递给客户端的文件是什么MIME类型。如:image/pjpeg
Content-Disposition:它的默认值为inline,表示直接在浏览器窗口打开(如文字和图片)。需要下载弹框则使用attachment;filename=xxx
一个流:要下载的文件数据

public class Download1Servlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		String filename="C:/Users/Pictures/板绘临摹/IMG_3862.JPG";
		//通过文件名获取MIME类型
		String contentType=this.getServletContext().getMimeType(filename);
		String contentDisposition="attachment;filename=a.jpg";
		//一个流
		FileInputStream input=new FileInputStream(filename);
		//设置头
		response.setHeader("Content-Type", contentType);
		response.setHeader("Content-Disposition", contentDisposition);
		//获取绑定了客户端的流
		ServletOutputStream output=response.getOutputStream();
		IOUtils.copy(input,output);
		input.close();
	}
}

下载的细节

显示在下载框的中文会乱码或不显示。
使用一个自己的类,根据不同浏览器处理编码:

public class DownUtils {
	public static String filenameEncoding(String filename, HttpServletRequest request) throws IOException {
		String agent = request.getHeader("User-Agent"); //获取浏览器
		if (agent.contains("Firefox")) {
			BASE64Encoder base64Encoder = new BASE64Encoder();
			filename = "=?utf-8?B?"
					+ base64Encoder.encode(filename.getBytes("utf-8"))
					+ "?=";
		} else if(agent.contains("MSIE")) {
			filename = URLEncoder.encode(filename, "utf-8");
		} else {
			filename = URLEncoder.encode(filename, "utf-8");
		}
		return filename;
	}
}

使用:

public class Download1Servlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		String filename="C:/Users/徐钰洁&杰泥粉/Pictures/板绘临摹/IMG_3862.JPG";
		//解决中文名乱码问题
		String framename=DownUtils.filenameEncoding("哇啦啦.jpg",request);//here
		//通过文件名获取MIME类型
		String contentType=this.getServletContext().getMimeType(filename);
		String contentDisposition="attachment;filename="+framename;
		//一个流
		FileInputStream input=new FileInputStream(filename);
		//设置头
		response.setHeader("Content-Type", contentType);
		response.setHeader("Content-Disposition", contentDisposition);
		//获取绑定了客户端的流
		ServletOutputStream output=response.getOutputStream();
		IOUtils.copy(input,output);
		input.close();
	}
}

邮件

JavaMail:是java提供的一组API,用来发送和接收邮件

邮件协议:
SMTP:发邮件协议
POP3:收邮件协议
IMAP:收发邮件协议

服务器名称:
SMTP服务器的端口号为25,服务器名称为smtp.xxx.xxx,例如smtp.163.com、smtp.sina.com
POP3服务器的端口号为110,服务器名称为pop3.xxx.xxx,如pop3.163.com、pop3.sina.com

javamail发邮件

  • 导包:mail.jar、activation.jar
  • 核心类:
    Session:得到了它,表示已经与服务器连接上了,与Connection作用类似
    MimeMessage:表示一个邮件对象,可以调用它的setFrom(),设置发件人、收件人、主题、正文
    Transport:它只有一个功能:发邮件

举例:

public class Demo1 {
	public void fun1() throws AddressException, MessagingException {
		//1.得到Session
		Properties props=new Properties();
		props.setProperty("mail.host", "smtp.163.com");//设置服务器主机名
		props.setProperty("mail.smtp.auth", "true");//设置需要认证
		Authenticator auth=new Authenticator() {//实现认证器接口
			public PasswordAuthentication getPasswordAuthentication() {
				return new PasswordAuthentication("hhhh","123");//用户名和密码
			}
		};
		Session session=Session.getInstance(props,auth);
		//2.创建MimeMessage
		MimeMessage msg=new MimeMessage(session);
		msg.setFrom(new InternetAddress("jnf@163.com"));///设置发件人
		msg.setRecipients(RecipientType.TO, "jnf@126.com");//设置收件人
		msg.setRecipients(RecipientType.CC, "jnf@sohu.com");//设置抄送(围观者)
		msg.setRecipients(RecipientType.BCC, "jnf@sina.com");//设置暗送(秘密围观者) 		
		msg.setSubject("测试");//邮件标题
		msg.setContent("芜湖", "text/html;charset=utf-8");//邮件正文
		//3.发送
		Transport.send(msg);
	}
}

附件

当发送包含附件的邮件时,邮件体就为多部件形式

  1. 创建多一个多部件MimeMultipart
  2. 创建两个主体部件(MimeBodyPart),一个是文本内容,一个是附件
  3. 把MimeMultipart设置给MimeMessage的内容
public class Demo1 {
	public void fun1() throws AddressException, MessagingException, IOException {
		//1.得到Session
		Properties props=new Properties();
		props.setProperty("mail.host", "smtp.163.com");
		props.setProperty("mail.smtp.auth", "true");
		Authenticator auth=new Authenticator() {
			public PasswordAuthentication getPasswordAuthentication() {
				return new PasswordAuthentication("hhhh","123");
			}
		};
		Session session=Session.getInstance(props,auth);
		//2.创建MimeMessage
		MimeMessage msg=new MimeMessage(session);
		msg.setFrom(new InternetAddress("jnf@163.com"));///设置发件人
		msg.setRecipients(RecipientType.TO, "jnf@126.com");//设置收件人
		msg.setRecipients(RecipientType.CC, "jnf@sohu.com");//设置抄送(围观者)
		msg.setRecipients(RecipientType.BCC, "jnf@sina.com");//设置暗送(秘密围观者) 		
		msg.setSubject("测试附件");
		MimeMultipart list=new MimeMultipart();
		//创建主体并设置内容
		MimeBodyPart part1=new MimeBodyPart();
		part1.setContent("包含附件,芜湖", "text/html;charset=utf-8");
		list.addBodyPart(part1);
		//创建第二个主体和并设置内容(绑定文件)
		MimeBodyPart part2=new MimeBodyPart();
		part2.attachFile(new File("f:/xx.jpg"));
		part2.setFileName(MimeUtility.encodeText("测试附件.jpg"));//设置显示的文件名并处理乱码
		msg.setContent(list);
		//3.发送
		Transport.send(msg);
	}
}

如果测试时报javax.mail.AuthenticationFailedException: 535 authentication failed,检查:

  • props.setProperty()设置的smtp是否正确
  • 使用的邮箱是否开启了smtp服务。开启后得到的授权码填入PasswordAuthentication(用户名,密码)的密码中
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值