H5異步上傳文件

最近項目上有遇到需要在手機APP端嵌入HTML5頁面并選取照片上傳绘盟,網(wǎng)上有很多方式實現(xiàn)麻惶,原本使用了百度的webupload插件溶推,但是需要依賴flash碍脏,所以我使用了H5自帶的FileReader API來讀取文件流轉(zhuǎn)換成base64字符串,然后使用ajax無刷新方式上傳到服務(wù)器卖擅。

HTML5頁面編寫

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
    <script type="text/javascript" src="assets/aries/lib/jquery.js"></script>
    <script type="text/javascript" src="js/json2/json2.js"></script>
    <style>
        .divImg{ border:1px solid #000; width:auto; height:auto;max-width: 100%;max-height: 100%}
        .divImg img{width:100px; height:100px}
    </style>
    <script type="text/javascript">
        var imgFiles = null;//用于存放base64字符串的數(shù)組

        $(function(){
            var input = $("#imgFile");
            var result,div;

            if(typeof FileReader==='undefined'){
                input.setAttribute('disabled','disabled');
                return alert("抱歉鸣奔,你的瀏覽器不支持 FileReader");
            }else{
                $("#imgFile").bind('change',readFile);
            }

            function readFile(){
                imgFiles = new Array();
                for(var i=0;i<this.files.length;i++){
                    var imgName = $("#imgFile").val().toLocaleLowerCase();
                    if (!imgName.match(/.jpg|.jpeg|.gif|.png|.bmp/i)){  //判斷上傳文件格式
                        return alert("上傳的圖片格式不正確,請重新選擇");
                    }
                    var reader = new FileReader();
                    reader.readAsDataURL(this.files[i]);
                    var picId = 0;
                    reader.onload = function(e){
                        imgFiles.push(this.result);//this.result就是圖片轉(zhuǎn)換后的base64字符串
                        result = '<div id="preview'+picId+'" class="divImg">![]('+this.result+')</div>';
                        div = document.createElement('div');
                        div.innerHTML = result;
                        document.getElementById('body').appendChild(div);//插入dom樹
                        picId++;
                    }
                }
            }

            $('#btnUpload').bind('click',uploadImg);
            $('#btnReset').bind('click',reset);
        })

        /**
         * 上傳文件
         */
        function uploadImg(){
            for(var i=0;i<imgFiles.length;i++){
                var base64 = imgFiles[i];
                $.ajax({
                    url : 'http://192.168.1.102:8083/uploadFile',
                    type : 'post',
                    contentType: 'application/x-www-form-urlencoded; charset=utf-8',
                    async: false,
                    cache: false,
                    data : base64,
                    success : function(data){
                        console.log(data)
                        var result = JSON.parse(data);
                        if(result.isSuccess){
                            alert('第'+(i+1)+'張圖片上傳成功');
                            $('#preview'+i).remove();
                        }else{
                            alert('第'+(i+1)+'張圖片上傳失敗:'+result.resultDesc);
                        }
                    }
                })
            }
            reset();
        }

        /**
         * 重置表單
         */
        function reset(){
            $('#imgFile').val('');
            $('.divImg').each(function(index,domEle){
                $(this).remove();
            })
        }

    </script>
</head>
<body id="body">
<form id="imgForm" action="http://localhost:8083/uploadFile" method="post" enctype="multipart/form-data"></form>
<label>請選擇一個圖像文件:</label>
<input type="file" id="imgFile" name="imgFile" accept="image/*" multiple />

<button id="btnUpload">上傳圖片</button>
<button id="btnReset">重置</button>
</body>
</html>

服務(wù)端處理

后臺使用原生的Java Servlet來接收處理

從HttpServletRequest中讀取前端傳來的InputStream并轉(zhuǎn)換成字符串

/**
     * 從HttpServletRequest中讀取前端傳來的InputStream并轉(zhuǎn)換成base64字符串
     *
     * @param request
     * @return
     * @throws Exception
     */
    private String getBase64Str(HttpServletRequest request) throws Exception {
        try {
            String imgStr = getRequestPayload(request);
            if (imgStr == null || "".equals(imgStr)) {
                return null;
            }
            /**
             * 由于前端轉(zhuǎn)過來的數(shù)據(jù)格式為data:image/jpeg;base64,圖像base64字符串,因此需要處理一下
             */
            final String subStr = "base64,";
            String base64Str = imgStr.substring(imgStr.indexOf(subStr) + subStr.length(), imgStr.length());
            return base64Str;
        } catch (Exception ex) {
            throw ex;
        }
    }

    /**
     * 從Request中讀取二進制數(shù)據(jù)流,并轉(zhuǎn)換成字符串
     * @param request
     * @return
     * @throws Exception
     */
    private String getRequestPayload(HttpServletRequest request) throws Exception{
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;
        try {
            reader = request.getReader();
            char[] buff = new char[1024];
            int len;
            while ((len = reader.read(buff)) != -1) {
                sb.append(buff, 0, len);
            }
        } catch (IOException e) {
            throw e;
        }finally {
            if(reader != null){
                reader.close();
            }
        }
        return sb.toString();
    }

將base64字符串轉(zhuǎn)換成圖片并寫入磁盤惩阶,最終完整的代碼如下

import net.sf.json.JSONObject;
import org.apache.commons.io.IOUtils;
import sun.misc.BASE64Decoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

/**
 * Created by qianlong on 2017/1/12.
 */
public class FileUploadServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        service(req,resp);
    }

    public void service(HttpServletRequest req, HttpServletResponse res)
            throws IOException, ServletException {

        JSONObject result = new JSONObject();
        res.setContentType("text/html;charset=UTF-8");

        // Create path components to save the file
        final String path = "/home/bluecoffee/upload";//保存路徑可以從配置文件中讀取
        final PrintWriter writer = res.getWriter();

        try {
            String base64Str = this.getBase64Str(req);
            if(base64Str == null || base64Str.equals("")){
                result.put("isSuccess", false);
                result.put("resultDesc", "請選擇需要上傳的圖片");
                writer.print(result.toString());
                return;
            }
            System.out.println("base64Str=" + base64Str);
            //生成一個新的文件名
            String newImgName = path + File.separator + System.currentTimeMillis() + ".jpg";
            boolean isWrite = this.writeImage(base64Str, newImgName);
            if (isWrite) {
                result.put("isSuccess", true);
                result.put("resultDesc", "圖片上傳成功");
                writer.print(result.toString());
            }else{
                result.put("isSuccess", false);
                result.put("resultDesc", "寫入磁盤文件失敗");
                writer.print(result.toString());
            }
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
            result.put("isSuccess", false);
            result.put("resultDesc", "圖片不存在:"+ex);
            writer.print(result.toString());
        } catch (Exception ex){
            ex.printStackTrace();
            result.put("isSuccess", false);
            result.put("resultDesc", "圖片上傳失敗:"+ex);
            writer.print(result.toString());
        }

    }

    /**
     * 從HttpServletRequest中讀取前端傳來的InputStream并轉(zhuǎn)換成真正的圖片base64字符串
     *
     * @param request
     * @return
     * @throws Exception
     */
    private String getBase64Str(HttpServletRequest request) throws Exception {
        try {
            String imgStr = getRequestPayload(request);
            if (imgStr == null || "".equals(imgStr)) {
                return null;
            }
            /**
             * 由于前端轉(zhuǎn)過來的數(shù)據(jù)格式為data:image/jpeg;base64,圖像base64字符串,因此需要處理一下
             */
            final String subStr = "base64,";
            String base64Str = imgStr.substring(imgStr.indexOf(subStr) + subStr.length(), imgStr.length());
            return base64Str;
        } catch (Exception ex) {
            throw ex;
        }
    }

    /**
     * 從Request中讀取二進制數(shù)據(jù)流,并轉(zhuǎn)換成字符串
     * @param request
     * @return
     * @throws Exception
     */
    private String getRequestPayload(HttpServletRequest request) throws Exception{
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;
        try {
            reader = request.getReader();
            char[] buff = new char[1024];
            int len;
            while ((len = reader.read(buff)) != -1) {
                sb.append(buff, 0, len);
            }
        } catch (IOException e) {
            throw e;
        }finally {
            if(reader != null){
                reader.close();
            }
        }
        return sb.toString();
    }
    
    /**
     * 將base64字符串轉(zhuǎn)換成圖片并按新文件名寫入磁盤,
     * @param base64Str
     * @param newImgName
     * @return
     * @throws Exception
     */
    private boolean writeImage(String base64Str, String newImgName) throws Exception {
        if (base64Str == null) // 圖像數(shù)據(jù)為空
            return false;

        BASE64Decoder decoder = new BASE64Decoder();
        OutputStream out = null;
        try {
            // Base64解碼
            byte[] bytes = decoder.decodeBuffer(base64Str);
            for (int i = 0; i < bytes.length; ++i) {
                if (bytes[i] < 0) {// 調(diào)整異常數(shù)據(jù)
                    bytes[i] += 256;
                }
            }
            //將圖片寫入磁盤
            out = new FileOutputStream(newImgName);
            out.write(bytes);
            return true;
        } catch (Exception e) {
            return false;
        } finally {
            if (out != null) {
                out.flush();
                out.close();
            }
        }
    }

}

web.xml中配置

    <servlet>
        <servlet-name>FileUploadServlet</servlet-name>
        <servlet-class>com.bluecoffee.web.servlets.FileUploadServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>FileUploadServlet</servlet-name>
        <url-pattern>/pc/uploadFile</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>FileUploadServlet</servlet-name>
        <url-pattern>/phone/uploadFile</url-pattern>
    </servlet-mapping>

小結(jié)

該示例在iPhone的safari瀏覽器挎狸、小米手機的瀏覽器測試過,還需要在更多移動端測試琳猫。下一步準備加入在移動端壓縮base64數(shù)據(jù)伟叛,提高傳輸效率。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末脐嫂,一起剝皮案震驚了整個濱河市统刮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌账千,老刑警劉巖侥蒙,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異匀奏,居然都是意外死亡鞭衩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門娃善,熙熙樓的掌柜王于貴愁眉苦臉地迎上來论衍,“玉大人,你說我怎么就攤上這事聚磺∨魈ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵瘫寝,是天一觀的道長蜒蕾。 經(jīng)常有香客問我,道長焕阿,這世上最難降的妖魔是什么咪啡? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮暮屡,結(jié)果婚禮上撤摸,老公的妹妹穿的比我還像新娘。我一直安慰自己褒纲,他們只是感情好愁溜,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著外厂,像睡著了一般冕象。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上汁蝶,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天渐扮,我揣著相機與錄音,去河邊找鬼掖棉。 笑死墓律,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的幔亥。 我是一名探鬼主播耻讽,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼帕棉!你這毒婦竟也來了针肥?” 一聲冷哼從身側(cè)響起饼记,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎慰枕,沒想到半個月后具则,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡具帮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年博肋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜂厅。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡匪凡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出掘猿,到底是詐尸還是另有隱情病游,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布术奖,位于F島的核電站礁遵,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏采记。R本人自食惡果不足惜佣耐,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望唧龄。 院中可真熱鬧兼砖,春花似錦、人聲如沸既棺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丸冕。三九已至耽梅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胖烛,已是汗流浹背眼姐。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留佩番,地道東北人众旗。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像趟畏,于是被迫代替她去往敵國和親贡歧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容