檔案上傳原理實現
檔案上傳原理實現
2010年09月07日 18:22:00 一步一個腳印 閱讀數:23920 標籤: stringnullconstructorbyteclassiterator 更多
個人分類: 3001_基礎綜合知識2001_程式語言20011_Java
版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/ybygjy/article/details/5869158
檔案上傳原理實現
客戶端瀏覽器是怎樣上傳資料的呢?伺服器端如何接收上傳的檔案資料?
級別: 初級
2004 年 9 月 01 日
文章主要描述http表單上傳二進位制資料流規範的簡單實現
(一)關於Form表單上傳檔案規範
總結個人在對新技術、新事物的學習和解決問題的過程,深刻體會到多理解掌握技術基礎理論知識再加上相應的實踐,的確能幫助我們在解決某些問題的時候起到事半功倍的效果。
以前上傳檔案類似的功能都是採用第三方元件來做的,真的是基於介面程式設計了。不出問題還好,真要是出現問題解決起來太不舒服了,往往屬於那種拆了西牆補東牆的策略。最近,在做檔案上傳時學習了一些 關於html>form上傳資料的格式規範,依據人家定義的規範做了一些簡單的工作。。。算是實現了個小輪子吧。
(二)實現
1、規則
1.1 上傳資料塊的分割規則
基於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.2 注意在後臺從request中取得分割串少兩個--,在看下面的原始資料你會發現流的最後是以--結束的。1.3 上傳的原始資料串,本來中文字元是亂碼的。為了清晰一些使用字符集UTF-8轉了下碼。
[xhtml] view plain copy
|
1.4 小結
基於1.3小節可以非常容易總結歸納出html-->form元素內容。有兩個檔案型別元素,三個text元素(其中一個元素是textarea)
2、操作順序流程描述
|
3、實現程式碼
需要明確注意的一個問題是關於request.getInputStream();獲取請求體資料流不可用的問題,見示例程式碼:
[java] view plain copy
- /*我是這麼認為的在獲取流之前,已經通過第三方應用伺服器相關對ServletRequest介面實現解析過流了,所以我們再次取請求流內容時會有問題。。在參與.NET裝置與JavaWeb互動時也遇到過此型別的問題。*/
- /*這就要求我們如果是自己解析request流內容資料,在解析之前不可以呼叫getParameter(String paramName)相關的需要用到解析流的行為方法。*/
- /*以上僅個人理解、個人意見,如有不對請多多指正。*/
- request.getParameter("USER_CODE");
- InputStream ins = request.getInputStream();
- byte[] buff = new byte[1024];
- int count = -1;
- while ((count = ins.read(buff))!=-1) {
- System.out.println(new String(buff,0, count, "UTF-8"));
- }
3.1、類圖
3.2、程式碼內容
入口Servlet
[java] view plain copy
- package org.ybygjy.web;
- import java.io.IOException;
- import java.util.Iterator;
- 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.ybygjy.web.comp.FileItem;
- import org.ybygjy.web.comp.FileMgrComp;
- import org.ybygjy.web.utils.WrapperRequest;
- /**
- * 處理基於WebHttp的請求/響應
- * @author WangYanCheng
- * @version 2010-1-10
- */
- public class Servlet extends HttpServlet {
- /** default serial */
- private static final long serialVersionUID = 1L;
- /**
- * {@inheritDoc}
- */
- public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
- IOException {
- request.setCharacterEncoding("UTF-8");
- List<FileItem> fileItemArr = null;
- String contentType = request.getContentType();
- if (null != contentType && contentType.startsWith(WrapperRequest.ContentType.FORM_DATA.getType())) {
- fileItemArr = new FileMgrComp().doAnalyse(WrapperRequest.getInstance(request), response);
- for (Iterator<FileItem> iterator = fileItemArr.iterator(); iterator.hasNext();) {
- System.out.println(iterator.next());
- }
- }
- }
- }
工具類
|
[java] view plain copy
- package org.ybygjy.web.utils;
- import javax.servlet.http.HttpServletRequest;
- /**
- * RequestUtils
- * @author WangYanCheng
- * @version 2010-1-10
- */
- public class WrapperRequest {
- /**ContentType*/
- public enum ContentType {
- /**MIME型別-二進位制資料標記*/
- FORM_DATA("multipart/form-data"),
- /**MIME型別-標準編碼格式標記*/
- FORM_URLENCODE("application/x-www-form-urlencoded"),
- /**MIME型別-文字格式標記*/
- FORM_TEXT("text/plain");
- /**inner type*/
- private final String type;
- /**
- * Constructor
- * @param str type
- */
- private ContentType(String str) {
- this.type = str;
- }
- /**
- * getter Type
- * @return type
- */
- public String getType() {
- return this.type;
- }
- }
- /**ContentType*/
- private String contentType;
- /**request*/
- private HttpServletRequest request = null;
- /**
- * Constructor
- * @param request request
- */
- private WrapperRequest(HttpServletRequest request) {
- this.request = request;
- }
- /**
- * getInstance
- * @param request request
- * @return wrapperRequest
- */
- public static final WrapperRequest getInstance(HttpServletRequest request) {
- return new WrapperRequest(request);
- }
- /**
- * get no wrapper Request
- * @return request request/null
- */
- public HttpServletRequest getRequest() {
- return this.request;
- }
- /**
- * getContentType
- * @return contentTypeStr/null
- */
- public String getContentType() {
- if (null == this.contentType) {
- this.contentType = null == this.request ? null : this.request.getContentType();
- }
- return this.contentType;
- }
- /**
- * 是否二制資料格式
- * @return true/false
- */
- public boolean isBinaryData() {
- boolean rtnBool = false;
- String tmpStr = getContentType();
- if (tmpStr.contains(ContentType.FORM_DATA.getType())) {
- rtnBool = true;
- }
- return rtnBool;
- }
- /**
- * 取得內容界定符
- * @return rtnStr/null
- */
- public String getBoundary() {
- String rtnStr = null;
- String tmpType = getContentType();
- if (null != tmpType) {
- rtnStr = tmpType.matches("^[//s//S]*boundary=[//s//S]*$") ? tmpType.split("boundary=")[1] : null;
- }
- return "--".concat(rtnStr);
- }
- /**
- * 測試入口
- * @param args 引數列表
- */
- public static void main(String[] args) {
- // WrapperRequest.ContentType[] cts = WrapperRequest.ContentType.values();
- // for (WrapperRequest.ContentType ct : cts) {
- // System.out.println(ct.getType());
- // }
- // System.out.println(WrapperRequest.getInstance(null).getBoundary());
- /*Matcher matcher = Pattern.compile("(//s)+").matcher("分/n/r割符");
- while (matcher.find()) {
- int count = matcher.groupCount();
- for (int i = 0; i < count; i++) {
- byte[] tmpByte = matcher.group(i).getBytes();
- for (int tmpI : tmpByte) {
- System.out.print(tmpI);
- }
- }
- }*/
- WrapperRequest wpInst = WrapperRequest.getInstance(null);
- wpInst.getBoundary();
- }
- }
基礎實現[java] view plain copy
- package org.ybygjy.web.comp;
- import java.io.BufferedOutputStream;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import org.ybygjy.web.utils.FileUtils;
- /**
- * 負責檔案的儲存解析
- * @author WangYanCheng
- * @version 2010-08-31
- */
- public class FileItem {
- /** 原始檔路徑 */
- private String srcFilePath;
- /** 原始檔全名稱 */
- private String srcFileFullName;
- /** 原始檔名稱 */
- private String srcFileName;
- /** 檔案轉儲路徑 */
- private String filePath;
- /** 檔名稱 */
- private String fileName;
- /** 檔案全名稱 */
- private String fileFullName;
- /** 上傳檔案引數名稱 */
- private String paramName;
- /** MIME Type */
- private String mimeType;
- /** 分割串 */
- private String boundaryStr;
- /**
- * Constructor
- * @param paramStr 引數名稱
- * @param fileStr 原始檔地址串
- * @param mimeType MIMEType
- * @param boundaryStr 分割約束
- */
- public FileItem(String paramStr, String fileStr, String mimeType, String boundaryStr) {
- String[] tmpStrArr = paramStr.split("=");
- this.setParamName(tmpStrArr[1].substring(1, tmpStrArr[1].length() - 1));
- tmpStrArr = fileStr.split("=");
- this.setSrcFilePath(tmpStrArr[1].substring(1, tmpStrArr[1].length() - 1));
- this.setMIME(mimeType);
- this.setBoundaryStr(boundaryStr);
- }
- /**
- * setFilePath
- * @param filePath filePath
- */
- public void setSrcFilePath(String filePath) {
- this.srcFilePath = filePath;
- if (this.srcFilePath != null && filePath.length() > 0) {
- this.srcFileFullName = new File(this.srcFilePath).getName();
- this.srcFileName = this.srcFileFullName.substring(0, this.srcFileFullName.indexOf('.'));
- }
- }
- /**
- * setMIME
- * @param mimeType mimeType
- */
- public void setMIME(String mimeType) {
- this.mimeType = mimeType;
- }
- /**
- * getMIME
- * @return mimeType mimeType
- */
- public String getMIME() {
- return this.mimeType;
- }
- /**
- * setBoundary
- * @param boundaryStr the boundaryStr to set
- */
- public void setBoundaryStr(String boundaryStr) {
- this.boundaryStr = boundaryStr;
- }
- /**
- * setParamName
- * @param paramName paramName
- */
- public void setParamName(String paramName) {
- this.paramName = paramName;
- }
- /**
- * getParamName
- * @return paramName
- */
- public String getParamName() {
- return this.paramName;
- }
- /**
- * 源上傳檔案全名稱
- * @return the srcFileName
- */
- public String getSrcFileFullName() {
- return srcFileFullName == null ? null : srcFileFullName.length() == 0 ? null : this.srcFileFullName;
- }
- /**
- * 源上傳檔名稱
- * @return the srcFileName
- */
- public String getSrcFileName() {
- return srcFileName;
- }
- /**
- * 檔案儲存路徑
- * @return the filePath
- */
- public String getFilePath() {
- return filePath;
- }
- /**
- * 取檔名稱
- * @return the fileName
- */
- public String getFileName() {
- return fileName;
- }
- /**
- * 取檔案全名稱
- * @return the fileFullName
- */
- public String getFileFullName() {
- return fileFullName;
- }
- /**
- * 轉儲檔案
- * @param ins ins
- * @throws IOException IOException
- */
- public void doStoreFile(InputStream ins) throws IOException {
- String id = String.valueOf(System.currentTimeMillis());
- File tmpFile = new File(FileUtils.getTmpFilePath(), id);
- this.filePath = tmpFile.getPath();
- this.fileFullName = tmpFile.getName();
- this.fileName = id;
- BufferedOutputStream bos = null;
- try {
- bos = new BufferedOutputStream(new FileOutputStream(tmpFile));
- this.doStoreFile(ins, bos);
- } catch (IOException ioe) {
- throw ioe;
- } finally {
- try {
- bos.close();
- } catch (IOException ioe) {
- ioe.printStackTrace();
- }
- }
- }
- /**
- * 儲存檔案
- * @param ins ins
- * @param bos bos
- * @throws IOException IOException
- */
- private void doStoreFile(InputStream ins, OutputStream bos) throws IOException {
- byte[] byteArr = new byte[this.boundaryStr.getBytes().length];
- try {
- int tmpI = -1;
- int tmpL = -1;
- ins.skip(2);
- while (((tmpI = ins.read()) != -1)) {
- if (13 == tmpI) {
- tmpL = ins.read();
- if (10 == tmpL && isBoundary(ins, byteArr)) {
- break;
- } else {
- bos.write(tmpI);
- bos.write(tmpL);
- if (10 == tmpL) {
- bos.write(byteArr);
- }
- continue;
- }
- }
- bos.write(tmpI);
- }
- bos.flush();
- } catch (IOException ioe) {
- throw ioe;
- }
- }
- /**
- * 檢驗是否邊界
- * @param ins ins
- * @param byteArr byteArr
- * @return true/false
- * @throws IOException IOException
- */
- private boolean isBoundary(InputStream ins, byte[] byteArr) throws IOException {
- if (null == this.boundaryStr) {
- return false;
- }
- boolean rtnFlag = false;
- int count = ins.read(byteArr);
- if (count != -1) {
- String str = new String(byteArr, 0, count);
- if (this.boundaryStr.equals(str)) {
- rtnFlag = true;
- }
- byteArr = str.getBytes();
- }
- return rtnFlag;
- }
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("FileItem [boundaryStr=");
- builder.append(boundaryStr);
- builder.append(", fileFullName=");
- builder.append(fileFullName);
- builder.append(", fileName=");
- builder.append(fileName);
- builder.append(", filePath=");
- builder.append(filePath);
- builder.append(", mimeType=");
- builder.append(mimeType);
- builder.append(", paramName=");
- builder.append(paramName);
- builder.append(", srcFileName=");
- builder.append(srcFileFullName);
- builder.append(", srcFilePath=");
- builder.append(srcFilePath);
- builder.append("]");
- return builder.toString();
- }
- }
|
[java] view plain copy
- package org.ybygjy.web.comp;
- 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
-