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数据,提高传输效率。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容