AJAX图片上传且上传前预览

来源:互联网 发布:贵州省大数据平台 编辑:程序博客网 时间:2024/05/20 16:12

图片上传前预览功能, 然后使用ajax上传到服务器.
这里写图片描述

用到CSS, AJAX, WEB_API以及Servlet的两个文件上传的jar包.
分别是: commons-fileupload-1.3.1.jarcommons-io-2.4.jar
可以在: http://archive.apache.org/dist/commons/ 下载到这2个jar包.

使用的一些WEB_API可以在 https://developer.mozilla.org/zh-CN/docs/Web/API 这里找到, PS: 这是个好地方, 如果之前没去过, 建议参观一下.

下面介绍具体的实现过程, 需要说明的地方我在注释上都有详细的说明.

<!--  Created by AL0N4k on 01/02/2017 0:46.--><!DOCTYPE html><html><head>    <meta charset="UTF-8">    <title>AJAX图片上传</title>    <style>        body {            color: #333;        }        p.title {            font-size: 30px;            width: 500px;            hegith: auto;            padding-left: 10px;            border-left: 5px solid dodgerblue;        }        div.big, div.middle, div.small {            display: inline-block;            margin-right: 10px;        }        .big img.avatar {            width: 200px;            height: 200px;        }        .middle img.avatar {            width: 100px;            height: 100px;        }        .small img.avatar {            width: 50px;            height: 50px;        }        .desc {            color: #999;        }        .wrapper {            position: relative;        }        .wrapper input {            width: 130px;            height: 38px;            cursor: pointer;        }        .wrapper .portrait-filebtn {            outline: none;            border: none;            padding: 0;            background: url("https://ss1.bdstatic.com/5KZ1bjqh_Q23odCf/static/princess/img/setportrait_btn_da8845e1.png") no-repeat 0 0px;        }        .wrapper .portrait-file {            opacity: 0;            position: absolute;            left: 0;            top: 0;        }        div#info {            padding: 10px 0;            font-size: 15px;        }    </style>    <script>        function ajaxSaveToServer(imgFile) {            var xhr = new XMLHttpRequest();            xhr.open("post", "saveImg", true);            /**             * XMLHttpRequest Level 2添加了一个新的接口FormData.             * 利用FormData对象,我们可以通过JavaScript用一些键值对来模拟一系列表单控件,             * 我们还可以使用XMLHttpRequest的send()方法来异步的提交这个"表单".             * 比起普通的ajax,使用FormData的最大优点就是我们可以异步上传一个二进制文件.             *             * 上面的解释很清楚了, 更多信息可以参考这里: https://developer.mozilla.org/zh-CN/docs/Web/API/FormData             */            var formData = new FormData();            /**             * 给当前FormData对象添加一个键/值对.             */            formData.append("img", imgFile);            // 后面的就是发送请求处理响应了.            xhr.send(formData);            xhr.onreadystatechange = function () {                var state = xhr.readyState;                var status = xhr.status;                if (state == 4 && status == 200) {                    alert(xhr.responseText);                }            }        }        window.onload = function () {            var fileInput = document.getElementById("fileImg");            var infoDiv = document.getElementById("info");            var avatarImgs = document.getElementsByClassName("avatar");            // 为文件选择input绑定一个change事件监听.            fileInput.addEventListener("change", function (event) {                /**                 * 所以这里的this指针是input对象                 * this.files获取的是WEB_API中的FileList对象, 这里不介绍那么多, 篇文太长.                 * 参考: https://developer.mozilla.org/zh-CN/docs/Web/API/FileList                 * */                var file = this.files[0];                 // 获取文件的大小且四舍五入后不保留小数.                var fileSize = (file.size / 1024).toFixed();                 // 获取文件名                var fileName = file.name;                 infoDiv.innerHTML = "<b>Size: </b>" + fileSize + "Kib<br />" + "<b>Name: </b>" + fileName;                /**                 * WEB_API FileReader对象, 这里不介绍那么多, 篇文太长.                 * 参考: https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader                 */                var fileReader = new FileReader();                 /**                 * 开始读取指定的File对象中的内容.                 * 将File对象中的文件内容读入到FileReader对象result属性中.                 */                fileReader.readAsDataURL(file);                // 当fileReader载入的时候, 我们把读入的数据给下面我们定义的img标签的src属性中.                fileReader.onload = function (event) {                    // 我这里循环是因为我弄了3种尺寸的预览图.                    for (var i = 0; i < avatarImgs.length; i++) {                        var avatarImg = avatarImgs[i];                        /**                         * 上面说了读入的会把File对象的文件内容读到result属性,                          * 所以这里获取result的值赋给img标签的src属性, 就可以实现在上传前图片预览的功能了.                         *                         * 这里的result指的是: 读取到File对象中的图片文件内容.                         * 这个属性中的值的格式就是一个经过base64编码的图片数据URL.                         * 差不多就是这样的格式, 应该都见过的.                          * 了解更多请看: https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/result                         *                          * data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/7R+KUGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAADwcAVo                         */                        avatarImg.src = this.result;                        //avatarImg.src = event.target.result;                    }                    // ajax发送请求上传文件.                    ajaxSaveToServer(file);                }            }, false);        };    </script></head><body><p class="title">Choose Image</p><div class="wrapper">    <input type="button" class="portrait-filebtn"           value="Choose">    <input type="file" class="portrait-file" name="file"           id="fileImg" accept=".jpg,.png,.gif,.bmp,.jpeg">    <span class="pass-portrait-msg">Supporting formats: jpg、jpeg、gif、png、bmp</span></div><div id="info"></div><p class="title">Image Preview</p><div class="preview">    <div class="big">        <p class="desc">200x200 Pixel</p>        <img src="" class="avatar">    </div>    <div class="middle">        <p class="desc">100x100 Pixel</p>        <img src="" class="avatar">    </div>    <div class="small">        <p class="desc">50x50 Pixel</p>        <img src="" class="avatar">    </div></div></body></html>

上面在注释里js和web_api的介绍说完了, 这里说一下css代码相关的地方.
首先. class为wrapper的div, 需要设置position为relative, 然后将里面的第二个input, 也就是文件选择的input. 它的position设置为absolute, absolute会相关于第一个position为非static的父元素来定位. 所以这里就是相对于刚才那个class为container的div来定位, 然后我们让它left和top都为0, 让它紧贴着div, 然后再给它的透明度设置为0. 通过opacity: 0;来设置, 这样子做了之后他前面类型button的input就会”覆盖”它, 说是覆盖其实是因为透明度为0, 人的眼睛看不到了. 但是在页面上还是存在, 所以我们不能设置display为none这个样式. 因为我们需要能点击它.

其他的css就是我个人的一些处理, 没什么特别的地方. 上面说的就是核心处理的地方.

再然后我们来看服务端的代码.
服务端java代码, 我用的servlet配合2个jar包来实现上传.

package com.al0n4k.servlet;import org.apache.commons.fileupload.FileItem;import org.apache.commons.fileupload.FileUploadException;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.File;import java.io.IOException;import java.io.PrintWriter;import java.util.Arrays;import java.util.List;/** * Created on 01/02/2017 1:14. * @author AL0N4k */public class SaveFile extends HttpServlet {    // 文件上传路径    private static final String UPLOAD_DIRECTORY = "upload";    // 文件路径分隔符, linux为/, windows为\    private static final String File_SEPARATOR = File.separator;    // 允许的文件contentype类型.    private static final String[] ALLOWED_TYPE = { "image/jpg", "image/jpeg", "image/png", "image/gif", "image/bmp" };    /**     * 检查是否为允许的文件类型.     *      * @param contentType     * @return     */    private boolean checkContentype (String contentType) {        if ( contentType == null ) return false;        // 将数组转成list.        List<String> list = Arrays.asList(ALLOWED_TYPE);        // 查看是否存在于list中, 如果有任意一个存在, 那么就是合法的类型, 否则就是不合法的.        return list.contains(contentType);    }    protected void doPost (HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        request.setCharacterEncoding("utf-8");        response.setContentType("text/html;charset=utf-8");        PrintWriter writer = response.getWriter();        try {            // 如果不是二进制文件, 那么上传失败.            if ( !ServletFileUpload.isMultipartContent(request) ) {                writer.write("上传失败");                return;            }            DiskFileItemFactory factory = new DiskFileItemFactory();            ServletFileUpload fileUpload = new ServletFileUpload(factory);            List<FileItem> fileItems = fileUpload.parseRequest(request);            FileItem fileItem = fileItems.get(0);            String contentType = fileItem.getContentType();            if ( !checkContentype(contentType) ) {                writer.write("非法文件, 请选择正确的图片文件");                return;            }            // 获取上传文件的文件夹真实的物理路径.            String uploadPath = getServletContext().getRealPath(File_SEPARATOR + UPLOAD_DIRECTORY);            File uploadPathFile = new File(uploadPath);            // 文件夹不存在则创建文件夹.            if ( !uploadPathFile.exists() ) {                uploadPathFile.mkdir();            }            // 获取文件名.            String name = fileItem.getName();            // 如果文件名是完整的路径形式(火狐浏览器会存在这个现象), 那么截取出文件名.            if ( name.contains(File_SEPARATOR) ) {                name = name.substring(name.lastIndexOf(File_SEPARATOR) + 1);            }            // 文件夹 + 文件名            File imgFile = new File(uploadPath + File_SEPARATOR + name);            fileItem.write(imgFile);            // 删除临时文件, 这个方法也可以不调, 因为在FileItem对象实例被回收的时候会自动清空临时文件, 但框架注释说了, 为了确保万一最好调用一下.            fileItem.delete();            if ( imgFile.exists() ) {                writer.write("上传成功!");            }        } catch ( FileUploadException e ) {            e.printStackTrace();        } catch ( Exception e ) {            e.printStackTrace();        } finally {            writer.flush();            writer.close();        }    }    protected void doGet (HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doPost(request, response);    }}

到此就结束了. 没什么技术含量, 主要是我写来玩的, 同时也只用于参考.
大家可以看一下上面用的几个WEB_API对象来了解更多, 以便按照自己的需求来开发.

0 0