`
hjqhezgh
  • 浏览: 40301 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

JAVA中文件上传下载知识点整理

阅读更多

先来看看文件上传的部分,首先我先介绍下要实现文件上传,对于页面的要求,实际上也就是表单要多加一些属性。

 

<h2>查看文件上传的请求正文</h2>
<form id="form1" method="POST" enctype="multipart/form-data" action="SeeRequestContentServlet">
	<input type="text" name="myText" value="test" /><br/>
	<input type="file" name="myFile"/><br/>
	<input type="submit" value="提交" /><br/>
</form>

 

首先,因为是文件上传,那么表单的method必须指定为POST,然后必须告诉服务器,这是一次带有附件的请求,因此必须加上enctype="multipart/form-data"的表单属性以及对应的值。对于文件控件,相应的就是修改input标签的type属性为file就可以了。SeeRequestContentServlet这是一个用于处理我这次文件上传的Servlet文件,详细的东西大家可以查看附件。

想要知道待会要介绍的fileupload这个jar包,大家必须先知道一个带有附件的请求的请求正文到底是什么样子的,这样有利于大家的理解。

所以接下来演示,不使用jar包,来分析带附件的请求的请求正文。请看SeeRequestContentServlet的doPost方法。

 

public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

	System.out.println("本次请求正文长度为:" + request.getHeader("Content-Length"));

	System.out.println("本次请求类型及表单域分隔符:" + request.getContentType());

	ServletInputStream sis = request.getInputStream();

	ServletOutputStream sos = response.getOutputStream();

	byte[] b = new byte[2048];

	@SuppressWarnings("unused")
	int count = 0;
		
	while ((count = sis.read(b)) != -1) {
		sos.write(b, 0, count);
	}	
	
	sos.flush();
	sos.close();
}

 

代码实际上很简单,就是一些基础的流操作,我们来看一下运行结果,并做一个分析。

 

下面是我即将上传的文件:


我的表单数据:


 

请看上传后在网页上看到的结果:

 

-----------------------------80233217916595
Content-Disposition: form-data; name="myText"

test
-----------------------------80233217916595
Content-Disposition: form-data; name="myFile"; filename="测试文件.txt"
Content-Type: text/plain

�����ļ�����
-----------------------------80233217916595--

 

以及我的控制台打印结果:

 

本次请求正文长度为:310
本次请求类型及表单域分隔符:multipart/form-data; boundary=---------------------------80233217916595

 

待会在处理乱码问题,这个不是关键。

通过控制台打印结果,以及网页的输出结果,我们发现了,对于含有附件的请求正文来说,表单域之前会有一个---------------------------80233217916595作为分隔符,并且对于附件域来说,还会有额外的 filename="测试文件.txt"的文本来标示文件在上传时使用的文件名。

于是大家如果能够知道请求正文的格式组成之后,剩下来要做的不就是根据分隔符以及相应的一些固定的字符串如filename,name,通过字符串操作来获取请求正文中大家需要的数据了。感觉很繁琐吧,这也是为什么大家现在完成文件上传功能是都是使用Apache提供的fileupload的jar包来实现的原因了。不过个人感觉理解了原理,再去应用,对于学习是很有好处的,所以写了这个例子。

接下来,解释下出现乱码的原因,很简单,就是编码不统一引起的,由于我编程时习惯上都把编码设置为UTF-8,包括上传表单的页面编码,而text文档在中文系统中的默认编码是GBK(关于这个大家可以用Editplus求证),所以在打印响应内容的时候,由于编码的不兼容引起了乱码,解决方式很简单,用Editplus打开文件,重新编码即可。

原始编码:


修改后编码:


网页结果:

 

-----------------------------20169232712042
Content-Disposition: form-data; name="myText"

test
-----------------------------20169232712042
Content-Disposition: form-data; name="myFile"; filename="测试文件.txt"
Content-Type: text/plain

测试文件内容
-----------------------------20169232712042--

 

另外我们也发现了分隔符是随机生成的。以上例子并没有真正完成文件上传,只是想从请求正文的内容来分析文件上传原理,实际上大家自己有耐心地去做一些字符串截取就可以实现文件上传了,这里就省略了。进入fileupload的使用部分。

 

先看页面代码,跟之前的实际上没有太大区别,只是改了个action的地址。

 

<h2>使用fileupload组件实现文件上传</h2>
<form id="form2" method="POST" enctype="multipart/form-data" action="FileUploadServlet">
	<input type="text" name="myText" value="test" /><br/>
	<input type="file" name="myFile"/><br/>
	<input type="submit" value="提交" /><br/>
</form>

 

来看FileUploadServlet的doPost方法

 

@SuppressWarnings("unchecked")
public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

	try {
		FileItemFactory factory = new DiskFileItemFactory();
		ServletFileUpload upload = new ServletFileUpload(factory);
		upload.setHeaderEncoding("utf8");//支持中文文件名
		List<FileItem> items = upload.parseRequest(request);
			
		Iterator<FileItem> iter = items.iterator();
		while (iter.hasNext()) {
			FileItem item = iter.next();
			if (item.isFormField()) {
			    System.out.println("查找到一个普通文本数据");
			     System.out.println("该文本数据的name为:"+item.getFieldName());
			    System.out.println("该文本数据的value为:"+item.getString());
			    System.out.println();
			 } else {
			     System.out.println("查找到一个二进制数据");
			       System.out.println("该文件表单name为:"+item.getFieldName());
			     System.out.println("该文件文件名为:"+item.getName());
			     System.out.println("该文件文件类型为:"+item.getContentType());
			     System.out.println("该文件文件大小为:"+item.getSize());
			     System.out.println();
			     File uploadedFile = new File(this.getServletContext().getRealPath("fileupload")+"\\"+item.getName());
			     item.write(uploadedFile);
			}
		}
	} catch (FileUploadException e) {
		e.printStackTrace();
	} catch (Exception e) {
		e.printStackTrace();
	}
		
	response.sendRedirect("success.jsp");

}

 

唯一要注意的就是那个可以支持中文文件名的代码,其他的跟Apache官方提供的代码就一致了。

有了上面的代码相信要实现多文件上传也是很容易的事情了。

 

最后来个带上传进度的文件上传。这里需要AJAX的知识,不懂的朋友可以先去了解下。

 

先看页面的代码

 

<h2>文件上传显示进度</h2>
<iframe id='target_upload' name='target_upload' src='' style='display: none'></iframe>
<form id="form3" method="POST" enctype="multipart/form-data" action="AJAXFileUploadServlet" target="target_upload">
	<input type="file" name="myFile"/><br/>
	<input type="button" value="提交" id="myButton"/><br/>
</form>
<div id="show"></div>

 

细心的朋友发现多一个iframe和div,iframe的作用是,当我们要显示上传进度的时候,一定是要求页面不能刷新的,因此你会发现form的target属性等于iframe的name属性,也就是说,当form提交数据的时候,当前页面不会刷新,而是iframe刷新了,而iframe又是dispaly:none的,所以看起来页面好像是没有发生跳转。div的作用则是用于显示上传进度。

 

接下来关注服务器的代码部分,想想其实就可以发现,服务器需要两个Servlet,一个正在用于文件上传,而一个则是用来返回AJAX关于上传进度的数据返回。

 

这里要介绍fileupload中很好用的一个接口,ProgressListener,她可以用来监听文件的上传进度,使用方法很简单,即在文件上传的代码中加入

 

FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("utf8");// 支持中文文件名
			
MyProgressListener myProgressListener = new MyProgressListener(request);
upload.setProgressListener(myProgressListener);//进行文件上传进度监听

 

MyProgressListener是实现ProgressListener的一个自定义类。下面是ProgressListener的代码。

package com.mison.fileupload;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.fileupload.ProgressListener;

public class MyProgressListener implements ProgressListener {

	private HttpSession session;

	private FileUploadStatus fileUploadStatus;

	public MyProgressListener(HttpServletRequest request) {
		session = request.getSession();
	}

	/***************************************************************************
	 * pBytesRead - The total number of bytes, which have been read so far.
	 * pContentLength - The total number of bytes, which are being read. May be
	 * -1, if this number is unknown. 
	 * pItems - The number of the field, which is
	 * currently being read. (0 = no item so far, 1 = first item is being read,
	 * ...)
	 */
	@Override
	public void update(long pBytesRead, long pContentLength, int pItems) {
		System.out.println("监听器被调用");
		fileUploadStatus = (FileUploadStatus) (session.getAttribute("fileUploadStatus") == null ? new FileUploadStatus()
				: session.getAttribute("fileUploadStatus"));
		
		fileUploadStatus.setPBytesRead(pBytesRead);
		fileUploadStatus.setPContentLength(pContentLength);
		fileUploadStatus.setPItems(pItems);
		
		session.setAttribute("fileUploadStatus", fileUploadStatus);
	}

}

 

这样就了解了吧。在update方法中,将上传进度的有关信息保存到session对象中,那么AJAX请求就可以访问session来获取相应的上传进度了。

 

来看AJAX的服务器端代码

 

package com.mison.fileupload;

import java.io.IOException;
import java.io.PrintWriter;

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

import net.sf.json.JSONObject;

@SuppressWarnings("serial")
public class SeeProgressServlet extends HttpServlet {

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

	@Override
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		
		FileUploadStatus fileUploadStatus = (FileUploadStatus) request
				.getSession().getAttribute("fileUploadStatus");
		
		JSONObject jsonObject = JSONObject.fromObject(fileUploadStatus);
		PrintWriter out = response.getWriter();
		out.println(jsonObject.toString());
		out.flush();
		out.close();
		
	}

}

 

上面使用了json的包,来实现对象到json字符串的转换,不懂的自己搜索下。

 

最后就是客户端的js代码了

 

<script type="text/javascript" src="js/jquery126.js"></script>
<script type="text/javascript">
	$(function(){
		$("#myButton").click(function(){
			$("#show").html("");
			$(this).attr("disabled",true);
			$("#form3").submit();
			setTimeout("showProgress()",500);
		});
	});
		
	function showProgress(){
		$.getJSON("SeeProgressServlet",function(json){
			$("#show").html("上传进度:"+(json.PBytesRead/json.PContentLength)*100+"%");
			if(json.PBytesRead == json.PContentLength){
				$("#show").html($("#show").html()+"上传结束~");
				$("#myButton").attr("disabled",false);
			}else{
				setTimeout("showProgress()",500);
			}
		});
	}
</script>

 

本地上传的话,弄个100M的东西,貌似效果比较明显,截个图给大家看下。

 



 

 不过网页很卡就是,求可以优化的做法。

*********************************************************************************

第二部分是文件下载,相比简单很多了,终于可以松口气。

 

网页代码

 

<h2>文件下载</h2>
<form id="form4" method="POST" action="FileDownloadServlet">
请输入文件名:<input type="text" name="fileName"/><br/>
<input type="submit" value="提交" /><br/>
</form>

 

Servlet代码

 

package com.mison.filedown;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

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

@SuppressWarnings("serial")
public class FileDownloadServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		this.doPost(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		String fileName = new String(request.getParameter("fileName").getBytes(
				"ISO-8859-1"), "UTF-8");

		FileInputStream fis = new FileInputStream(new File(getServletContext()
				.getRealPath("WEB-INF/download")
				+ "\\" + fileName));
		
		System.out.println("客户端类型:" + request.getHeader("User-Agent"));

		if (request.getHeader("User-Agent").contains("Firefox")) {
			response.addHeader("content-disposition", "attachment;filename="
					+ request.getParameter("fileName"));
		} else if (request.getHeader("User-Agent").contains("MSIE")) {
			response.addHeader("content-disposition", "attachment;filename="
					+ java.net.URLEncoder.encode(fileName, "UTF-8"));
		}

		//相应的逻辑操作
		
		ServletOutputStream sos = response.getOutputStream();

		int count = 0;

		byte[] bytes = new byte[1024];

		while ((count = fis.read(bytes)) != -1) {
			sos.write(bytes, 0, count);
		}

		sos.flush();

		sos.close();

	}

}

 

比较有意思的是关于下载时指定中文文件名的部分,由于ie和火狐的处理方式不一样,因此写了个判断分支。对于ie来讲,使用URLEncoder这个类将中文编码为16进制的asci码,这样ie客户端可以自己进行转码,可惜火狐没有这功能;火狐的处理方式是将中文的字符用ISO-8859-1编码,火狐会在客户端自己进行转码,同样的,这样的功能ie也没有,真蛋疼。

 

好了。附上工程附件,有需要的朋友可以拿去测试

  • 大小: 30.5 KB
  • 大小: 12.5 KB
  • 大小: 13.5 KB
  • 大小: 11.1 KB
  • 大小: 13.9 KB
  • 大小: 12.4 KB
分享到:
评论
6 楼 Y1198700955 2014-12-21  
java.io.FileNotFoundException: E:\MyEclipse\workspace\.metadata\.me_tcat\webapps\TestFileUploadAndDownLoad\fileupload\C:\Users\Administrator\Desktop\成为一名合格的java工程师必须要做到以下几点_百度经验.png (文件名、目录名或卷标语法不正确。)
5 楼 H4X0R 2014-08-08  
总结的非常详细,学习学习。
4 楼 飘香神栗 2014-04-25  
请问楼主,如果上传的时候要实现断点续传,应该怎么实现?能否给个思路
3 楼 crazyj 2013-07-06  
参考参考,下载部分chrome没有做判断
2 楼 磐石依旧 2013-06-01  
非常不错,顶起!
1 楼 chinalian 2012-05-18  
很棒的总结。谢谢

相关推荐

    Java 上传方法的整理

    首先,Java文件上传主要依赖于Servlet API,特别是`Part` 接口,这是在Servlet 3.0及以上版本中引入的,用于处理multipart/form-data类型的POST请求。当用户通过表单提交包含文件的请求时,服务器端可以通过`...

    2024 Java面试宝典合集

    面试可能涉及请求映射、模型视图、拦截器、异常处理、数据绑定、文件上传下载等。 8. **12万字 java 面经总结**:这是一份详尽的面试经验汇总,涵盖了大量实际面试中可能出现的问题,包括但不限于Java性能优化、...

    java新闻发布系统Java实用源码整理learns

    6. **文件上传下载**:新闻系统可能涉及文件上传,如图片、文档等,这需要用到Servlet API或者第三方库如Commons FileUpload。 7. **前端技术**:HTML、CSS、JavaScript(可能使用jQuery或其他库如Vue.js或React)...

    java断点续传,刚刚整理出来多线程处理

    Java 断点续传技术是网络传输中常用的一种机制,特别是在大文件下载或上传时,如果因为网络中断或其他原因导致传输失败,可以借助断点续传功能从上次中断的地方继续,而无需重新开始。在Java中实现断点续传,通常...

    Java面试笔记(上传文件名有限制)

    这些知识点在【Java面试笔记】中可能都有涉及,通过阅读【11,22.txt】这两个文档,求职者可以进一步深入学习和理解,为面试做好充分准备。同时,鼓励社区成员分享更多的资源,共同提升Java技术水平。

    jersey+spring+srpingmvc实现上传

    【标题】:“jersey+spring+...通过这个示例,开发者可以了解到如何利用Java生态系统中的这些流行框架来实现文件上传功能,并为自己的项目提供参考。同时,理解并实践这些知识点,也有助于提升对Web开发的整体理解。

    java框架整理

    以下是对Java框架整理的一些关键知识点: 1. **静态(static)与实例(his)依赖**: - `static` 关键字用于声明类级别的变量和方法,不依赖于类的实例,可以在没有创建对象的情况下直接调用。 - `his` 是实例...

    java 图片管理系统代码及设计.ppt

    下面是从该文件中提取的知识点: 1. Java 语言简介 Java 是一种简单的、跨平台的、面向对象的、分布式的、解释的、健壮的安全的、结构的中立的、可移植的、性能很优异的多线程的、动态的语言。Java 语言的出现彻底...

    java2018面试宝典

    本文是一份关于Java面试的全面复习资料,涵盖了2018年Java面试中常见的知识点。接下来,我将详细解析文档中提到的各个知识点,以及它们在实际面试中的应用。 **算法**: - 算法是面试中考察逻辑思维能力的重要部分...

    java+web音乐翻唱网站+j2ee平台Java实用源码整理learns

    这一主题涵盖了许多Java Web开发的重要知识点,包括但不限于: 1. **J2EE平台**:Java 2 Enterprise Edition(J2EE)是Java平台上的一个标准,用于构建可扩展的、企业级的分布式应用。它包含了多种组件模型,如...

    JAVA 五年的工作经验和学习笔记

    这里需要说明的时,该文档是本人5年工作经验的积累,文档中大部分知识点来源于实际工作中的总结,(除了JAR等资源文件外)其代码都是可运行的,还有一部分知识来源于网络或者其他书籍,这里做一些收集,使该体系更加...

    上传一个Java项目资源

    ### Java基础知识详解 #### 1. Java平台与版本 - **JDK与JRE的区别**: - **JDK (Java Development Kit)**:是Java开发工具包,它包含了JRE及一系列开发工具...希望这些知识点能帮助你在Java学习之旅上更进一步!

    JSP编程小技巧-已整理 (例:文件上传,图片验证码,JSP标准动作.....)

    在IT行业中,JSP(JavaServer Pages)是一种用于创建动态网页的技术,它允许开发者将Java代码嵌入到HTML页面中,以实现服务器...在实际项目中,理解并熟练运用这些知识点,对于提升个人技能和解决实际问题非常有帮助。

    整理编写的几个Java小游戏合集涵盖了JAVA多方面的知识点.zip

    该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请...

    Java实现的FTP连接与数据浏览程序Java实用源码整理learns

    下面将详细讲解这些关键知识点: 1. **FTP连接**: - **FTPClient类**:Apache Commons Net库中的FTPClient类是FTP连接的核心,用于建立和管理FTP会话。 - **连接设置**:包括主机名、端口号、用户名和密码,可以...

    [上传下载]拓网文件上传共享系统 v1.0_topwangupload.zip

    拓网文件上传共享系统v1.0可能包含了以下关键知识点: 1. **Web服务器技术**:系统通常基于Web服务器运行,如Apache或Nginx,提供HTTP/HTTPS协议支持,使用户通过浏览器进行访问和操作。 2. **前端技术**:用户...

    SpringMVC文件上传

    SpringMVC文件上传知识点整理: SpringMVC框架是Java EE开发中常用的轻量级Web框架,其提供了强大的文件上传功能,能够处理多种文件上传的场景。本文档将详细讲解SpringMVC文件上传的实现原理、基本步骤、注意事项...

    java作业管理系统设计(源代码+文档).zip

    8. **文件上传与下载**:作业的提交和查看可能涉及文件操作。使用Servlet或第三方库如Apache Commons FileUpload可以处理文件上传,而文件下载则需要处理响应头信息。 9. **错误处理与日志记录**:良好的错误处理和...

Global site tag (gtag.js) - Google Analytics