文件上传原理实现
客户端浏览器是怎样上传数据的呢?服务器端如何接收上传的文件数据?
级别: 初级
2004 年 9 月 01 日
文章主要描述http表单上传二进制数据流规范的简单实现
(一)关于Form表单上传文件规范
总结个人在对新技术、新事物的学习和解决问题的过程,深刻体会到多理解掌握技术基础理论知识再加上相应的实践,的确能帮助我们在解决某些问题的时候起到事半功倍的效果。
以前上传文件类似的功能都是采用第三方组件来做的,真的是基于接口编程了。不出问题还好,真要是出现问题解决起来太不舒服了,往往属于那种拆了西墙补东墙的策略。最近,在做文件上传时学习了一些 关于html>form上传数据的格式规范,依据人家定义的规范做了一些简单的工作。。。算是实现了个小轮子吧。
(二)实现
- 1、规则
- 1.1 上传数据块的分割规则
- 1.2 注意在后台从request中取得分割串少两个--,在看下面的原始数据你会发现流的最后是以--结束的。
- 1.3 上传的原始数据串,本来中文字符是乱码的。为了清晰一些使用字符集UTF-8转了下码。
- 1.4 小结
基于html form表单上传的数据都是以类似-----------------------------7da3c8e180752{0x130x10}这样的分割符来标记一块数据的起止,可不要忘记后面的两个换行符。关于换行符有三种,如下:
操作系统 | 换行符描述 | 原始标记 | ascii码 | 十六进制 |
Window | Window的换行符是两个 | //r//n | 1310 | 0x0d0x0a |
Unix | Unix的换行符是一个 | //n | 10 | 0x0a |
Mac OS | Mac OS的换行符是一个 | //r | 13 | 0x0d |
这块没有对Unix、MacOS上做测试,只在Window上测试了换行是两个(0x0d0x0a) |
基于1.3小节可以非常容易总结归纳出html-->form元素内容。有两个文件类型元素,三个text元素(其中一个元素是textarea)
2、操作顺序流程描述
|
3、实现代码
需要明确注意的一个问题是关于request.getInputStream();获取请求体数据流不可用的问题,见示例代码:
3.1、类图
3.2、代码内容
- 入口Servlet
- 工具类
- 基础实现
-
回页首 import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.ybygjy.web.utils.WrapperRequest;
/**
* 负责对文件实体进行的操作
* @author WangYanCheng
* @version 2010-8-27
*/
public class FileMgrComp {/** serialNumber */
private static final long serialVersionUID = 5563862601527965192L;
/** boundaryStr */
private String boundaryStr;
/** ifcArray */
private List<FileItem> ifcArray;/**
* Constructor
*/
public FileMgrComp() {
ifcArray = new ArrayList<FileItem>();
}/**
* doService
* @param wrRequest wrRequest
* @param response response
* @return rtnList rtnList/null
* @throws IOException IOException
*/
public List<FileItem> doAnalyse(WrapperRequest wrRequest, HttpServletResponse response) throws IOException {
doInnerTest(wrRequest.getRequest());
List<FileItem> fileArr = null;
if (wrRequest.isBinaryData()) {
this.boundaryStr = wrRequest.getBoundary();
if (null != boundaryStr) {
fileArr = doAnalyseBinaryData(wrRequest);
}
}
return fileArr;
}/**
* 分析存储二进制数据
* @param wrRequest wrRequest
* @return fileItemArr fileItemArr
*/
private List<FileItem> doAnalyseBinaryData(WrapperRequest wrRequest) {
BufferedInputStream bins = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
bins = new BufferedInputStream(wrRequest.getRequest().getInputStream());
int tmpI = -1;
int tmpL = -1;
while ((tmpI = bins.read()) != -1) {
if (tmpI == 13) {
tmpL = (bins.read());
if (tmpL == 10) {
if (baos.size() == 0) {
continue;
}
FileItem fi = analyseFileInput(baos, bins);
if (fi != null) {
ifcArray.add(fi);
}
baos.reset();
continue;
}
baos.write(tmpI);
baos.write(tmpL);
}
baos.write(tmpI);
}
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
if (null != bins) {
try {
baos.close();
bins.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return this.getIfcArray();
}/**
* 解析验证上传内容是否文件类型
* @param os outStream
* @param ins insStream
* @return ifcInst ifcInst/null
*/
private FileItem analyseFileInput(ByteArrayOutputStream os, InputStream ins) {
String tmpStr = null;
try {
tmpStr = os.toString("UTF-8");
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
FileItem ifcIns = null;
if (tmpStr.indexOf("filename") != -1) {
String[] tmpStrArr = tmpStr.split(";");
if (tmpStrArr.length > 2) {
// 取MIME文件类型
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
doRead(ins, baos);
tmpStr = baos.toString();
if (tmpStr.startsWith("Content-Type:")) {
ifcIns = new FileItem(tmpStrArr[1].trim(), tmpStrArr[2].trim(),
tmpStr.split(":")[1].trim(), this.boundaryStr);
if (ifcIns.getSrcFileFullName() != null) {
ifcIns.doStoreFile(ins);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
baos.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
}
return ifcIns;
}/**
* doRead
* @param ins ins
* @param baos baos
* @throws IOException IOException
*/
private void doRead(InputStream ins, ByteArrayOutputStream baos) throws IOException {
int tmpI = -1;
while ((tmpI = ins.read()) != -1) {
if (tmpI == 13) {
tmpI = ins.read();
if (tmpI == 10) {
break;
} else {
baos.write(13);
baos.write(tmpI);
continue;
}
}
baos.write(tmpI);
}
}/**
* getIfcArray
* @return the ifcArray
*/
public List<FileItem> getIfcArray() {
return ifcArray;
}/**
* innerTest
* @param request request
*/
private void doInnerTest(HttpServletRequest request) {
Map<String, Object> testInfo = new HashMap<String, Object>();
testInfo.put("AuthType", request.getAuthType());
testInfo.put("CharacterEncoding", request.getCharacterEncoding());
testInfo.put("ContentLength", request.getContentLength());
testInfo.put("ContentType", request.getContentType());
testInfo.put("ContextPath", request.getContextPath());
testInfo.put("HeaderNames", request.getHeaderNames());
testInfo.put("LocalAddr", request.getLocalAddr());
testInfo.put("LocalName", request.getLocalName());
testInfo.put("PathInfo", request.getPathInfo());
testInfo.put("RequestedSessionId", request.getRequestedSessionId());
testInfo.put("UserPrincipal", request.getUserPrincipal());
org.ybygjy.test.TestUtils.doPrint(testInfo);
}
}
|
(三)资源