H5异步上传文件

最近项目上有遇到需要在手机APP端嵌入HTML5页面并选取照片上传,网上有很多方式实现,原本使用了百度的webupload插件,但是需要依赖flash,所以我使用了H5自带的FileReader API来读取文件流转换成base64字符串,然后使用ajax无刷新方式上传到服务器。

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字符串的数组

        $(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就是图片转换后的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>

服务端处理

后台使用原生的Java Servlet来接收处理

从HttpServletRequest中读取前端传来的InputStream并转换成字符串

/**
     * 从HttpServletRequest中读取前端传来的InputStream并转换成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;
            }
            /**
             * 由于前端转过来的数据格式为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中读取二进制数据流,并转换成字符串
     * @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字符串转换成图片并写入磁盘,最终完整的代码如下

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并转换成真正的图片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;
            }
            /**
             * 由于前端转过来的数据格式为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中读取二进制数据流,并转换成字符串
     * @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字符串转换成图片并按新文件名写入磁盘,
     * @param base64Str
     * @param newImgName
     * @return
     * @throws Exception
     */
    private boolean writeImage(String base64Str, String newImgName) throws Exception {
        if (base64Str == null) // 图像数据为空
            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) {// 调整异常数据
                    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>

小结

该示例在iPhone的safari浏览器、小米手机的浏览器测试过,还需要在更多移动端测试。下一步准备加入在移动端压缩base64数据,提高传输效率。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容