Vaadin与JQuery FileUpload

时间:2014-07-22 11:06:12

标签: file-upload vaadin image-uploading jquery-file-upload vaadin7

我想用 Vaadin 创建 FileUploader 。但我需要在normal Vaadin Upload上获得更多功能。

  1. 美观且易于管理(但可选)
  2. 上传时快速而且从未失败
  3. 包含进度条
  4. 显示预览
  5. 多文件上传支持
  6. 上传文件大小和类型限制
  7. 拖放
  8. 客户端图片可调整大小(这是我的主要功能,因为我上传的所有文件都是图片)
  9. 有一个插件MultiFileUpload。是的,它非常适合我的大多数要求,但不适用于客户端大小的图像大小调整。所以我决定使用JQuery FileUpload,因为它支持Client side Image Resizing

    我使用vaadin Window上传图片。但是我在创建窗口时遇到了问题,分别很难创建每个HTML元素(可能是我的exp )。所以我使用CustomLayout with HTML来轻松创建和编辑图像上传器窗口的设计。

    下面是我的自定义布局HTML文件。 (两个脚本是用于图像预览的模板

    <script id="template-upload" type="text/x-tmpl">
    {% for (var i=0, file; file=o.files[i]; i++) { %}
    <tr class="template-upload">
        <td width="100px" align="center">
            <span class="preview"></span>
        </td>
        <td width="400px" align="center">
            <p class="name">{%=file.name%}</p>
            {% if (!o.files.error) { %}
                <div class="progress progress-success progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><div class="bar" style="width:0%;"></div></div>
            {% } %}
            {% if (file.error) { %}
                <div><span class="label label-important">Error</span> {%=file.error%}</div>
            {% } %}
        </td>
        <td width="100px" align="center">
            {% if (!i) { %}
                <button style="display: none;" class="start" type="button">
                    <span>Start</span>
                </button>
                <div class="v-button v-widget cancel" type = "button">
                    <span class="v-button-wrap" style="color: red;">
                        <span class="v-button-caption">Cancel</span>
                    </span>
                </div>
            {% } %}
            <br>
            {%=o.formatFileSize(file.size)%}
        </td>
    </tr>
    {% } %}
    </script>
    <!-- The template to display files available for download -->
    <script id="template-download" type="text/x-tmpl">
    {% for (var i=0, file; file=o.files[i]; i++) { %}
    <tr class="template-download">
        <td width="100px" align="center">
            <span class="preview">
                {% if (file.path) { %}
                    <img src="../{%=file.path%}" width="100px">
                {% } %}
            </span>
        </td>
        <td width="400px" align="center">
            <p class="name">
                {%=file.name%}
            </p>
            {% if (file.error) { %}
                <div><span class="label label-important">Error</span> {%=file.error%}</div>
            {% } %}
        </td>
        <td width="100px" align="center">
            <span class="size">{%=o.formatFileSize(file.size)%}</span>
        </td>
    </tr>
    {% } %}
    </script>
    <table cellpadding="5" style="width: 100%;">
    <colgroup>
    <col>
    </colgroup>
    <tbody>
        <tr>
            <td width="90px">
                <div style="text-align: right; width: 120px;">UploadPhoto :</div>
            </td>
            <td>
                <div id="pnlProgress" aria-valuenow="0" aria-valuemax="100" aria-valuemin="0" style="display: none;" class="progress progressall progress-success progress-striped active">
                    <div style="width: 0%;" class="allbar" id="pnlProgressBar">&nbsp;</div>
                </div>
            </td>
        </tr>
        <tr>
            <td colspan="3">
                <div id="imageForm" style="width: 600px;">
                    <form id="fileupload">
                        <div style="margin-bottom: 10px; border: 1px solid #DDD; width: 600px; height: 300px; overflow: scroll">
                            <table cellspacing="0" cellpadding="5">
                                <tbody class="files"></tbody>
                            </table>
                        </div>
                        <div style="margin-bottom: 10px;" class="fileupload-buttonbar">
                            <div class="v-button v-widget btnPlus">
                                <span class="v-button-caption">Add Files</span> 
                                <input type="file" multiple="" name="files[]">
                            </div>
                            <div class="v-button v-widget start" type = "submit">
                                <span class="v-button-wrap">
                                    <span class="v-button-caption">StartUpload</span>
                                </span>
                            </div>
                            <div class="v-button v-widget cancel" type = "reset">
                                <span class="v-button-wrap">
                                    <span class="v-button-caption">Cancel All</span>
                                </span>
                            </div>
                        </div>
                        <div style="border: 1px solid #999; width: 600px; height: 100px;" id="dropZone">
                            <div class="carPhotoDropMsg">
                                Draft &amp; Drop Photos<br>(jpg, jpeg, png, gif only)
                            </div>
                        </div>
                    </form>
                </div>
            </td>
        </tr>
    </tbody>
    

    下面是ImageUpload窗口

    public final class ImageUploadDialog extends CustomComponent {
    private Window window;
    
    public void show() {
        UI.getCurrent().addWindow(window);
     // 123 is seq for save in database or other use
        Page.getCurrent().getJavaScript().execute("initImageuploader(123)");
    }
    
    public ImageUploadDialog() {
        CustomLayout layout = new CustomLayout("imageUploadLayout");
        window = new Window("Uploading Photos");
        window.center();
        window.setWidth("615px");
        window.setModal(true);
        window.setResizable(false);
        window.setClosable(true);
        window.setContent(layout);
    }
    }
    

    以下是我的upload.js文件,用于初始化我的图片上传器

    function initImageuploader(seq) {
    $('#fileupload').fileupload({
        url : 'photo/upload.html?s=' + seq,
        sequentialUploads : true,
        disableImageResize : false,
        imageMaxWidth : 1024,
        imageMaxHeight : 1024,
        previewCrop : true,
        dropZone : $("#dropZone"),
        acceptFileTypes : /(\.|\/)(gif|jpe?g|png)$/i,
        progress : function(e, data) {
            if (data.context) {
                var progress = data.loaded / data.total * 100;
                progress = Math.floor(progress);
                $('.progress').attr('aria-valuenow', progress);
                $('.progress').css('display', 'block');
                $('.bar').css('width', progress + '%');
            }
        },
        progressall : function(e, data) {
            var progress = data.loaded / data.total * 100;
            progress = Math.floor(progress);
            $('.progressall').attr('aria-valuenow', progress);
            $('.progressall').css('display', 'block');
            $('.allbar').css('width', progress + '%');
            if (progress > 20) {
                $('.allbar').text(progress + '% Completed');
            }
        },
        stop: function (e) {
            return;
        }
    });
    }
    

    你需要额外的javascripts文件用于图像上传器,我将它们导入我的UI类,如下所示

    @JavaScript({ "vaadin://themes/myproject/js/load-image.min.js",
        "vaadin://themes/myproject/js/tmpl.min.js",
        "vaadin://themes/myproject/js/jquery/jquery-1.10.1.min.js",
        "vaadin://themes/myproject/js/jquery/vendor/jquery.ui.widget.js",
        "vaadin://themes/myproject/js/jquery/jquery.iframe-transport.js",
        "vaadin://themes/myproject/js/jquery/jquery.fileupload.js",
        "vaadin://themes/myproject/js/jquery/jquery.fileupload-ui.js",
        "vaadin://themes/myproject/js/jquery/jquery.fileupload-process.js",
        "vaadin://themes/myproject/js/jquery/jquery.fileupload-image.js",
        "vaadin://themes/myproject/js/jquery/jquery.fileupload-validate.js",
        "vaadin://themes/myproject/js/canvas-to-blob.min.js",
        "vaadin://themes/myproject/js/upload.js" })
    @StyleSheet({ "vaadin://themes/myproject/css/jquery-ui-1.10.3.custom.min.css",
        "vaadin://themes/myproject/css/imageUpload.css" })
    public class EntryPoint extends UI {
    ..............
    }
    
      

    请注意JS文件订单!

    以下是我的图片上传窗口的自定义CSS文件( imageUpload.css

    table.upld-status {
    display: none;
    }
    .fileupload-buttonbar .btnPlus {
    float: left;
    position: relative;
    overflow: hidden;
    color: blue;
    text-align: center;
    margin-right : 10px;
    }
    .fileupload-buttonbar .btnPlus input {
    margin: 0px;
    position: absolute;
    top: 0px;
    right: 0px;
    line-height: 30px;
    font-size: 23px;
    direction: ltr;
    opacity: 0;
    }
    .carPhotoDropMsg {
    color: #DDD;
    font-size: 20pt;
    height: 82%;
    padding: 9px;
    text-align: center;
    }
    .progress {
    background-color: #F7F7F7;
    background-image: linear-gradient(to bottom, #F5F5F5, #F9F9F9);
    background-repeat: repeat-x;
    border-radius: 4px 4px 4px 4px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) inset;
    height: 17px;
    overflow: hidden;
    }
    .progress-success.progress-striped .bar, .progress-success.progress-striped .allbar, .progress
    striped .bar-success {
    background-color: #62C462;
    background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    }
    .progress.active .bar, .progress.active .allbar {
    animation: 2s linear 0s normal none infinite progress-bar-stripes;
    }
    .progress-success .bar, .progress-success .allbar, .progress .bar-success {
    background-color: #5EB95E;
    background-image: linear-gradient(to bottom, #62C462, #57A957);
    background-repeat: repeat-x;
    }
    .progress-striped .bar, .progress-striped .allbar {
    background-color: #149BDF;
    background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    background-size: 40px 40px;
    }
    .progress .bar, .progress .allbar {
    -moz-box-sizing: border-box;
    background-color: #0E90D2;
    background-image: linear-gradient(to bottom, #149BDF, #0480BE);
    background-repeat: repeat-x;
    box-shadow: 0 -1px 0 rgba(0, 0, 0, 0.15) inset;
    color: #FFFFFF;
    float: left;
    font-size: 12px;
    height: 100%;
    text-align: center;
    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    transition: width 0.4s ease 0s;
    width: 0;
    }
    

    我需要服务器端控制来保存图像。您需要两个广告apache-common-ioapache-common-fileupload。以下是这两个罐子的maven存储库。

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.2.2</version>
        </dependency>
    
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
    

    最后,下面是服务器端控制的代码。

    @WebServlet(value = "/photo/upload.html")
    public class UploadServletController extends HttpServlet {
    protected final void doPost(final HttpServletRequest request,
            final HttpServletResponse response) throws ServletException, IOException {
    
        response.setContentType("application/json");
        PrintWriter out = response.getWriter();
        FileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        List<FileItem> fields = null;
        try {
            fields = upload.parseRequest(request);
        }
        catch (FileUploadException e) {
            throw new RuntimeException("Error Parsing File Item " + e.getMessage(), e);
        }
        if (fields != null) {
            String message = uploadPhoto(request, fields);
            out.write(message);
        }
    }
    public final synchronized String uploadPhoto(final HttpServletRequest request,
            final List<FileItem> sessionFiles) {
    
        List<Map<String, Object>> ret = new ArrayList<Map<String, Object>>();
        for (FileItem item : sessionFiles) {
            if (!item.isFormField()) {
                Long seq = Long.parseLong(request.getParameter("s"));
    // get from vm arguments (eg:-DstaticDir=/Applications/springsource/workspace/myproject/src/main/webapp)
                String staticDir = System.getProperty("staticDir");
    
                Date today = new Date();
                SimpleDateFormat fmtYMD = new SimpleDateFormat("/yyyyMMdd/HH");
                SimpleDateFormat fmtHMS = new SimpleDateFormat("HHmmssS");
    
                String saveDir = "data/photo" + fmtYMD.format(today);
                String format = ".jpg";
                try {
                    format = item.getName().substring(item.getName().lastIndexOf("."), item.getName().length())
                            .toLowerCase();
                }
                catch (Exception e) {
                    // nothing to do!
                }
    
                String fileName = seq + "_" + fmtHMS.format(today) + format;
                Map<String, Object> res = new HashMap<String, Object>();
                // Save image in specify location
                String filePath = staticDir + "/" + saveDir;
                saveFile(filePath, fileName, item);
    
                res.put("seq", seq);
                res.put("path", saveDir + "/" + fileName);
                res.put("ext", format.substring(1));
    
                res.put("name", item.getName());
                res.put("size", item.getSize());
                ret.add(res);
            }
        }
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("files", ret);
        JSONObject obj = new JSONObject(result);
    
        return obj.toString();
    }
    public static String saveFile(final String filePath, final String fileName, final FileItem item) {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
        File imageFile = new File(file, fileName);
        try {
            item.write(imageFile);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        item.setFieldName(filePath + fileName);
        return item.toString();
    }
    }
    

    我知道我的代码可能存在风险和一些弱点。欢迎提出任何建议。但我相信新手有一些有用的东西(我也是新手)。抱歉格式太长,不好。

    最后一件事是我的问题......

    为什么预览图片(上传后不上传)会自动包含 url 而不是文件路径?我找不到图片错误

    "NetworkError: 404 Not Found - http://localhost:8080/myproject/VAADIN/themes/myTheme/data/photo/20140723/23/123_235918346.jpg"
    

    实际上,此图片路径应为data/photo/20140723/23/111_235918346.jpg。我不知道为什么前缀url http://localhost:8080/myproject/VAADIN/themes/myTheme/会自动包含(可能是由于我的CustomLayout HTML文件路径)?文件路径来自HTTP响应(使用JSON)。我认为这是由于VAADIN,因为它适用于我的GWT项目,或者可能是我错了。有什么建议 ?感谢您阅读我的问题。

1 个答案:

答案 0 :(得分:1)

我通过修复上传图片后的预览模板的 src 值来修复它...

<script id="template-download" type="text/x-tmpl">
{% for (var i=0, file; file=o.files[i]; i++) {; %}
<tr class="template-download">
    <td width="100px" align="center">
        <span class="preview">
            {% if (file.path) { %}
                <img src="/myproject/{%=file.path%}" width="100px">
            {% } %}
        </span>
    </td>
    <td width="400px" align="center">
        <p class="name">
            {%=file.name%}
        </p>
        {% if (file.error) { %}
            <div><span class="label label-important">Error</span> {%=file.error%}</div>
        {% } %}
    </td>
    <td width="100px" align="center">
        <span class="size">{%=o.formatFileSize(file.size)%}</span>
    </td>
</tr>
{% } %}
</script>

现在每个人都很好。如果您没有立即看到图像,请检查您的IDE(Eclipse或STS)设置如下

  

偏好&gt;一般&gt;工作区

并选中Refresh on accessRefresh using native hooks or polling复选框。