• jQuery File Upload 单页面多实例的实现


    jQuery File Upload 的 GitHub 地址:https://github.com/blueimp/jQuery-File-Upload

    插件描述:jQuery File Upload 是一个 jQuery 图片上传组件,支持多文件上传、取消、删除,上传前缩略图预览、列表显示图片大小,支持上传进度条显示。插件基于开放的标准,如 HTML5 和 JavaScript ,不需要额外的浏览器插件(例如使用Adobe 的 Flash ),在旧版浏览器中使用 XMLHttpRequest 上传文件。(参见:http://www.jq22.com/jquery-info230)。

    需求:在一个页面中包含多个插件实例。例如在生鲜电商网站后台的食谱添加/编辑页面,需要上传/编辑一道菜的食材和配料,就需要在页面中同时包含两个上传实例。插件的文档中有关于单页面多实例的介绍:Multiple File Upload Widgets on the same page,里面说明很简单,把 demo 中 index.html 的 form 表单的 id 改成 class,再简单修改 js/main.js 就可以了。

    在很天真的试过之后,发现根本不是这么回事,index.html 中文件域的 name 是写死的,根本无法满足单页面多实例的需求,只能对插件进行修改...而且在改完之后发现修改的工作量很大。

    代码是在 demo 的基础上,后端使用 php,删除了不需要的文件,比如其他后端语言处理程序。没有做数据验证和入库等处理,展示页面直接遍历文件夹读取文件。

    最终效果图:

    初始界面

    上传图片及刷新页面

    文件结构及主要修改文件

    js

    server

    /index.html

    <!DOCTYPE HTML>
    <!--
    /*
     * jQuery File Upload Plugin Demo 9.1.0
     * https://github.com/blueimp/jQuery-File-Upload
     *
     * Copyright 2010, Sebastian Tschan
     * https://blueimp.net
     *
     * Licensed under the MIT license:
     * http://www.opensource.org/licenses/MIT
     */
    -->
    <html lang="en">
    <head>
    <!-- Force latest IE rendering engine or ChromeFrame if installed -->
    <!--[if IE]>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <![endif]-->
    <meta charset="utf-8">
    <title>jQuery File Upload Demo</title>
    <meta name="description" content="File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads and client-side image resizing. Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- Bootstrap styles -->
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <!-- Generic page styles -->
    <link rel="stylesheet" href="css/style.css">
    <!-- blueimp Gallery styles -->
    <link rel="stylesheet" href="css/blueimp-gallery.min.css">
    <!-- CSS to style the file input field as button and adjust the Bootstrap progress bars -->
    <link rel="stylesheet" href="css/jquery.fileupload.css">
    <!-- <link rel="stylesheet" href="css/jquery.fileupload-ui.css"> -->
    <!-- CSS adjustments for browsers with JavaScript disabled -->
    <noscript><link rel="stylesheet" href="css/jquery.fileupload-noscript.css"></noscript>
    <noscript><link rel="stylesheet" href="css/jquery.fileupload-ui-noscript.css"></noscript>
    </head>
    <body>
    <div class="container">
        <!-- The file upload form used as target for the file upload widget -->
        <form class="fileupload" action="server/php" method="POST" enctype="multipart/form-data">
            <!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
            <div class="row fileupload-buttonbar">
                <div class="col-lg-7">
                    <!-- The fileinput-button span is used to style the file input field as button -->
                    <span class="btn btn-primary">食材</span>
                    <span class="btn btn-success fileinput-button">
                        <i class="glyphicon glyphicon-plus"></i>
                        <span>Add files...</span>
                        <input type="file" name="food[]" multiple>
                    </span>
                    <button type="submit" class="btn btn-primary start">
                        <i class="glyphicon glyphicon-upload"></i>
                        <span>Start upload</span>
                    </button>
                    <button type="reset" class="btn btn-warning cancel">
                        <i class="glyphicon glyphicon-ban-circle"></i>
                        <span>Cancel upload</span>
                    </button>
                    <button type="button" class="btn btn-danger delete">
                        <i class="glyphicon glyphicon-trash"></i>
                        <span>Delete</span>
                    </button>
                    <input type="checkbox" class="toggle">
                    <!-- The global file processing state -->
                    <span class="fileupload-process"></span>
                </div>
                <!-- The global progress state -->
                <div class="col-lg-5 fileupload-progress fade">
                    <!-- The global progress bar -->
                    <div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100">
                        <div class="progress-bar progress-bar-success" style="0%;"></div>
                    </div>
                    <!-- The extended global progress state -->
                    <div class="progress-extended"> </div>
                </div>
            </div>
            <!-- The table listing the files available for upload/download -->
            <table role="presentation" class="table table-striped"><tbody class="files"></tbody></table>     
        </form> 
        <!-- Multiple File Upload Widgets on the same page -->
        <form class="fileupload" action="server/php" method="POST" enctype="multipart/form-data">
            <!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
            <div class="row fileupload-buttonbar">
                <div class="col-lg-7">
                    <!-- The fileinput-button span is used to style the file input field as button -->
                    <span class="btn btn-primary">配料</span>
                    <span class="btn btn-success fileinput-button">
                        <i class="glyphicon glyphicon-plus"></i>
                        <span>Add files...</span>
                        <input type="file" name="batching[]" multiple>
                    </span>
                    <button type="submit" class="btn btn-primary start">
                        <i class="glyphicon glyphicon-upload"></i>
                        <span>Start upload</span>
                    </button>
                    <button type="reset" class="btn btn-warning cancel">
                        <i class="glyphicon glyphicon-ban-circle"></i>
                        <span>Cancel upload</span>
                    </button>
                    <button type="button" class="btn btn-danger delete">
                        <i class="glyphicon glyphicon-trash"></i>
                        <span>Delete</span>
                    </button>
                    <input type="checkbox" class="toggle">
                    <!-- The global file processing state -->
                    <span class="fileupload-process"></span>
                </div>
                <!-- The global progress state -->
                <div class="col-lg-5 fileupload-progress fade">
                    <!-- The global progress bar -->
                    <div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100">
                        <div class="progress-bar progress-bar-success" style="0%;"></div>
                    </div>
                    <!-- The extended global progress state -->
                    <div class="progress-extended"> </div>
                </div>
            </div>
            <!-- The table listing the files available for upload/download -->
            <table role="presentation" class="table table-striped"><tbody class="files"></tbody></table>     
        </form> 
    </div>
    <!-- The blueimp Gallery widget -->
    <div id="blueimp-gallery" class="blueimp-gallery blueimp-gallery-controls" data-filter=":even">
        <div class="slides"></div>
        <h3 class="title"></h3>
        <a class="prev">‹</a>
        <a class="next">›</a>
        <a class="close">×</a>
        <a class="play-pause"></a>
        <ol class="indicator"></ol>
    </div>
    <!-- The template to display files available for upload -->
    <script id="template-upload" type="text/x-tmpl">
    {% for (var i=0, file; file=o.files[i]; i++) { %}
        <tr class="template-upload fade">
            <td>
                <span class="preview"></span>
            </td>
            <td>
                <p class="name">{%=file.name%}</p>
                <strong class="error text-danger"></strong>
            </td>
            <td>
                <p class="size">Processing...</p>
                <div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><div class="progress-bar progress-bar-success" style="0%;"></div></div>
            </td>
            <td>
                {% if (!i && !o.options.autoUpload) { %}
                    <button class="btn btn-primary start" disabled>
                        <i class="glyphicon glyphicon-upload"></i>
                        <span>Start</span>
                    </button>
                {% } %}
                {% if (!i) { %}
                    <button class="btn btn-warning cancel">
                        <i class="glyphicon glyphicon-ban-circle"></i>
                        <span>Cancel</span>
                    </button>
                {% } %}
            </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 fade">
            <td>
                <span class="preview">
                    {% if (file.thumbnailUrl) { %}
                        <a href="{%=file.url%}" title="{%=file.name%}" download="{%=file.name%}" data-gallery><img src="{%=file.thumbnailUrl%}"></a>
                    {% } %}
                </span>
            </td>
            <td>
                <p class="name">
                    {% if (file.url) { %}
                        <a href="{%=file.url%}" title="{%=file.name%}" download="{%=file.name%}" {%=file.thumbnailUrl?'data-gallery':''%}>{%=file.name%}</a>
                    {% } else { %}
                        <span>{%=file.name%}</span>
                    {% } %}
                </p>
                {% if (file.error) { %}
                    <div><span class="label label-danger">Error</span> {%=file.error%}</div>
                {% } %}
            </td>
            <td>
                <span class="size">{%=o.formatFileSize(file.size)%}</span>
            </td>
            <td>
                {% if (file.deleteUrl) { %}
                    <button class="btn btn-danger delete" data-type="{%=file.deleteType%}" data-url="{%=file.deleteUrl%}"{% if (file.deleteWithCredentials) { %} data-xhr-fields='{"withCredentials":true}'{% } %}>
                        <i class="glyphicon glyphicon-trash"></i>
                        <span>Delete</span>
                    </button>
                    <input type="checkbox" name="delete" value="1" class="toggle">
                {% } else { %}
                    <button class="btn btn-warning cancel">
                        <i class="glyphicon glyphicon-ban-circle"></i>
                        <span>Cancel</span>
                    </button>
                {% } %}
            </td>
        </tr>
    {% } %}
    </script>
    <script src="js/jquery/1.10.2/jquery.min.js"></script>
    <!-- The jQuery UI widget factory, can be omitted if jQuery UI is already included -->
    <script src="js/vendor/jquery.ui.widget.js"></script>
    <!-- The Templates plugin is included to render the upload/download listings -->
    <script src="js/tmpl.min.js"></script>
    <!-- The Load Image plugin is included for the preview images and image resizing functionality -->
    <script src="js/load-image.all.min.js"></script>
    <!-- The Canvas to Blob plugin is included for image resizing functionality -->
    <script src="js/canvas-to-blob.min.js"></script>
    <!-- Bootstrap JS is not required, but included for the responsive demo navigation -->
    <script src="js/bootstrap/3.0.0/js/bootstrap.min.js"></script>
    <!-- blueimp Gallery script -->
    <script src="js/jquery.blueimp-gallery.min.js"></script>
    <!-- The Iframe Transport is required for browsers without support for XHR file uploads -->
    <script src="js/jquery.iframe-transport.js"></script>
    <!-- The basic File Upload plugin -->
    <script src="js/jquery.fileupload.js"></script>
    <!-- The File Upload processing plugin -->
    <script src="js/jquery.fileupload-process.js"></script>
    <!-- The File Upload image preview & resize plugin -->
    <script src="js/jquery.fileupload-image.js"></script>
    <!-- The File Upload audio preview plugin -->
    <script src="js/jquery.fileupload-audio.js"></script>
    <!-- The File Upload video preview plugin -->
    <script src="js/jquery.fileupload-video.js"></script>
    <!-- The File Upload validation plugin -->
    <script src="js/jquery.fileupload-validate.js"></script>
    <!-- The File Upload user interface plugin -->
    <script src="js/jquery.fileupload-ui.js"></script>
    <!-- The main application script -->
    <script src="js/main.js"></script>
    <!-- The XDomainRequest Transport is included for cross-domain file deletion for IE 8 and IE 9 -->
    <!--[if (gte IE 8)&(lt IE 10)]>
    <script src="js/cors/jquery.xdr-transport.js"></script>
    <![endif]-->
    </body> 
    <script>
        // $('.fileupload').fileupload({
        //     // Uncomment the following to send cross-domain cookies:
        //     //xhrFields: {withCredentials: true},
        //     url: 'server/php/?fields=shirt,sweater',
        // }); 
        var fields = 'food,batching';
        $('.fileupload').fileupload({
            url: 'server/php/?fields=' + fields,
            add: function (e, data) {
                // 不用点击直接上传
                var jqXHR = data.submit()
                    .success(function (result, textStatus, jqXHR) {})
                    .error(function (jqXHR, textStatus, errorThrown) {})
                    .complete(function (result, textStatus, jqXHR) {});
            }     
        });
      
        $.ajax({
            url: 'server/php/?fields=' + fields,
            success(data) {
                var dataObj = JSON.parse(data);
                $.each(dataObj, function(name, value) {               
                    $.each(value, function(k, v){
                        var item = '<tr class="template-download fade in">';
                        item += '<td><span class="preview"><a data-gallery="" download="' + v.name + '" title="' + v.name + '" href="' + v.url + '"><img src="' + v.thumbnailUrl + '"></a></span></td><td><p class="name"><a data-gallery="" download="' + v.name + '" title="' + v.url + '" href="' + v.url + '">' + v.name + '</a></p></td><td><span class="size">' + (v.size / 1000) + ' KB</span></td><td>';
                
                        item += '<button data-url="' + v.deleteUrl + '" data-type="' + v.deleteType + '" class="btn btn-danger delete" ><i class="glyphicon glyphicon-trash"></i><span>Delete</span></button>';
                        item += ' <input type="checkbox" class="toggle" value="1" name="delete"></td></tr>';
    
                        var $item = $(item);
                        $input_file = eval($("input[type='file'][name='" + name + "[]']"));
                        $item.appendTo($input_file.parents('.fileupload-buttonbar').siblings('.table-striped').children('.files'));                                         
                    });                    
                });                      
            }
        });
    </script>
    </html>
    

    说明:

    通过 fields 来配置文件中不同的文件域,多个文件域的 name 用逗号 , 隔开。这是修改后 demo 中唯一需要根据页面文件域 name 的不同要做配置地方

    var fields = 'food,batching';
    

      

    /js/jquery.fileupload-ui.js

    修改 $.widget('blueimp.fileupload', $.blueimp.fileupload, {}) 中 的 getFilesFromResponse

                getFilesFromResponse: function (data) {
                    var paramName = JSON.stringify(data.paramName);
                    if(paramName) {
                        var files = paramName.slice(2,-4);
                    }
                    // console.log(files,eval("data.result."+files));
                    if (data.result && $.isArray(eval("data.result."+files))) {
                        return eval("data.result."+files);
                    }
                    return [];
                },
    

    把写死的 files 改成页面中实际的 name

    完整文件:

    /*
     * jQuery File Upload User Interface Plugin 9.6.0
     * https://github.com/blueimp/jQuery-File-Upload
     *
     * Copyright 2010, Sebastian Tschan
     * https://blueimp.net
     *
     * Licensed under the MIT license:
     * http://www.opensource.org/licenses/MIT
     */
    
    /* jshint nomen:false */
    /* global define, window */
    
    (function (factory) {
        'use strict';
        if (typeof define === 'function' && define.amd) {
            // Register as an anonymous AMD module:
            define([
                'jquery',
                'tmpl',
                './jquery.fileupload-image',
                './jquery.fileupload-audio',
                './jquery.fileupload-video',
                './jquery.fileupload-validate'
            ], factory);
        } else {
            // Browser globals:
            factory(
                window.jQuery,
                window.tmpl
            );
        }
    }(function ($, tmpl) {
        'use strict';
    
        $.blueimp.fileupload.prototype._specialOptions.push(
            'filesContainer',
            'uploadTemplateId',
            'downloadTemplateId'
        );
    
        // The UI version extends the file upload widget
        // and adds complete user interface interaction:
        $.widget('blueimp.fileupload', $.blueimp.fileupload, {
    
            options: {
                // By default, files added to the widget are uploaded as soon
                // as the user clicks on the start buttons. To enable automatic
                // uploads, set the following option to true:
                autoUpload: false,
                // The ID of the upload template:
                uploadTemplateId: 'template-upload',
                // The ID of the download template:
                downloadTemplateId: 'template-download',
                // The container for the list of files. If undefined, it is set to
                // an element with class "files" inside of the widget element:
                filesContainer: undefined,
                // By default, files are appended to the files container.
                // Set the following option to true, to prepend files instead:
                prependFiles: false,
                // The expected data type of the upload response, sets the dataType
                // option of the $.ajax upload requests:
                dataType: 'json',
                
                // Error and info messages:
                messages: {
                    unknownError: 'Unknown error'  
                },
    
                // Function returning the current number of files,
                // used by the maxNumberOfFiles validation:
                getNumberOfFiles: function () {
                    return this.filesContainer.children()
                        .not('.processing').length;
                },
    
                // Callback to retrieve the list of files from the server response:
                getFilesFromResponse: function (data) {
                    var paramName = JSON.stringify(data.paramName);
                    if(paramName) {
                        var files = paramName.slice(2,-4);
                    }
                    // console.log(files,eval("data.result."+files));
                    if (data.result && $.isArray(eval("data.result."+files))) {
                        return eval("data.result."+files);
                    }
                    return [];
                },
    
                // The add callback is invoked as soon as files are added to the fileupload
                // widget (via file input selection, drag & drop or add API call).
                // See the basic file upload widget for more information:
                add: function (e, data) {
                    if (e.isDefaultPrevented()) {
                        return false;
                    }
                    var $this = $(this),
                        that = $this.data('blueimp-fileupload') ||
                            $this.data('fileupload'),
                        options = that.options;
                    data.context = that._renderUpload(data.files)
                        .data('data', data)
                        .addClass('processing');
                    options.filesContainer[
                        options.prependFiles ? 'prepend' : 'append'
                    ](data.context);
                    that._forceReflow(data.context);
                    that._transition(data.context);
                    data.process(function () {
                        return $this.fileupload('process', data);
                    }).always(function () {
                        data.context.each(function (index) {
                            $(this).find('.size').text(
                                that._formatFileSize(data.files[index].size)
                            );
                        }).removeClass('processing');
                        that._renderPreviews(data);
                    }).done(function () {
                        data.context.find('.start').prop('disabled', false);
                        if ((that._trigger('added', e, data) !== false) &&
                                (options.autoUpload || data.autoUpload) &&
                                data.autoUpload !== false) {
                            data.submit();
                        }
                    }).fail(function () {
                        if (data.files.error) {
                            data.context.each(function (index) {
                                var error = data.files[index].error;
                                if (error) {
                                    $(this).find('.error').text(error);
                                }
                            });
                        }
                    });
                },
                // Callback for the start of each file upload request:
                send: function (e, data) {
                    if (e.isDefaultPrevented()) {
                        return false;
                    }
                    var that = $(this).data('blueimp-fileupload') ||
                            $(this).data('fileupload');
                    if (data.context && data.dataType &&
                            data.dataType.substr(0, 6) === 'iframe') {
                        // Iframe Transport does not support progress events.
                        // In lack of an indeterminate progress bar, we set
                        // the progress to 100%, showing the full animated bar:
                        data.context
                            .find('.progress').addClass(
                                !$.support.transition && 'progress-animated'
                            )
                            .attr('aria-valuenow', 100)
                            .children().first().css(
                                'width',
                                '100%'
                            );
                    }
                    return that._trigger('sent', e, data);
                },
                // Callback for successful uploads:
                done: function (e, data) {
                    if (e.isDefaultPrevented()) {
                        return false;
                    }
                    var that = $(this).data('blueimp-fileupload') ||
                            $(this).data('fileupload'),
                        getFilesFromResponse = data.getFilesFromResponse ||
                            that.options.getFilesFromResponse,
                        files = getFilesFromResponse(data),
                        template,
                        deferred;
                    if (data.context) {
                        data.context.each(function (index) {
                            var file = files[index] ||
                                    {error: 'Empty file upload result'};
                            deferred = that._addFinishedDeferreds();
                            that._transition($(this)).done(
                                function () {
                                    var node = $(this);
                                    template = that._renderDownload([file])
                                        .replaceAll(node);
                                    that._forceReflow(template);
                                    that._transition(template).done(
                                        function () {
                                            data.context = $(this);
                                            that._trigger('completed', e, data);
                                            that._trigger('finished', e, data);
                                            deferred.resolve();
                                        }
                                    );
                                }
                            );
                        });
                    } else {
                        template = that._renderDownload(files)[
                            that.options.prependFiles ? 'prependTo' : 'appendTo'
                        ](that.options.filesContainer);
                        that._forceReflow(template);
                        deferred = that._addFinishedDeferreds();
                        that._transition(template).done(
                            function () {
                                data.context = $(this);
                                that._trigger('completed', e, data);
                                that._trigger('finished', e, data);
                                deferred.resolve();
                            }
                        );
                    }
                },
                // Callback for failed (abort or error) uploads:
                fail: function (e, data) {
                    if (e.isDefaultPrevented()) {
                        return false;
                    }
                    var that = $(this).data('blueimp-fileupload') ||
                            $(this).data('fileupload'),
                        template,
                        deferred;                
                    if (data.context) {
                        data.context.each(function (index) {
                            if (data.errorThrown !== 'abort') {
                                var file = data.files[index];
                                file.error = file.error || data.errorThrown ||
                                    data.i18n('unknownError');
                                deferred = that._addFinishedDeferreds();
                                that._transition($(this)).done(
                                    function () {
                                        var node = $(this);
                                        template = that._renderDownload([file])
                                            .replaceAll(node);
                                        that._forceReflow(template);
                                        that._transition(template).done(
                                            function () {
                                                data.context = $(this);
                                                that._trigger('failed', e, data);
                                                that._trigger('finished', e, data);
                                                deferred.resolve();
                                            }
                                        );
                                    }
                                );
                            } else {
                                deferred = that._addFinishedDeferreds();
                                that._transition($(this)).done(
                                    function () {
                                        $(this).remove();
                                        that._trigger('failed', e, data);
                                        that._trigger('finished', e, data);
                                        deferred.resolve();
                                    }
                                );
                            }
                        });
                    } else if (data.errorThrown !== 'abort') {
                        data.context = that._renderUpload(data.files)[
                            that.options.prependFiles ? 'prependTo' : 'appendTo'
                        ](that.options.filesContainer)
                            .data('data', data);
                        that._forceReflow(data.context);
                        deferred = that._addFinishedDeferreds();
                        that._transition(data.context).done(
                            function () {
                                data.context = $(this);
                                that._trigger('failed', e, data);
                                that._trigger('finished', e, data);
                                deferred.resolve();
                            }
                        );
                    } else {
                        that._trigger('failed', e, data);
                        that._trigger('finished', e, data);
                        that._addFinishedDeferreds().resolve();
                    }
                },
                // Callback for upload progress events:
                progress: function (e, data) {
                    if (e.isDefaultPrevented()) {
                        return false;
                    }
                    var progress = Math.floor(data.loaded / data.total * 100);
                    if (data.context) {
                        data.context.each(function () {
                            $(this).find('.progress')
                                .attr('aria-valuenow', progress)
                                .children().first().css(
                                    'width',
                                    progress + '%'
                                );
                        });
                    }
                },
                // Callback for global upload progress events:
                progressall: function (e, data) {
                    if (e.isDefaultPrevented()) {
                        return false;
                    }
                    var $this = $(this),
                        progress = Math.floor(data.loaded / data.total * 100),
                        globalProgressNode = $this.find('.fileupload-progress'),
                        extendedProgressNode = globalProgressNode
                            .find('.progress-extended');
                    if (extendedProgressNode.length) {
                        extendedProgressNode.html(
                            ($this.data('blueimp-fileupload') || $this.data('fileupload'))
                                ._renderExtendedProgress(data)
                        );
                    }
                    globalProgressNode
                        .find('.progress')
                        .attr('aria-valuenow', progress)
                        .children().first().css(
                            'width',
                            progress + '%'
                        );
                },
                // Callback for uploads start, equivalent to the global ajaxStart event:
                start: function (e) {
                    if (e.isDefaultPrevented()) {
                        return false;
                    }
                    var that = $(this).data('blueimp-fileupload') ||
                            $(this).data('fileupload');
                    that._resetFinishedDeferreds();
                    that._transition($(this).find('.fileupload-progress')).done(
                        function () {
                            that._trigger('started', e);
                        }
                    );
                },
                // Callback for uploads stop, equivalent to the global ajaxStop event:
                stop: function (e) {
                    if (e.isDefaultPrevented()) {
                        return false;
                    }
                    var that = $(this).data('blueimp-fileupload') ||
                            $(this).data('fileupload'),
                        deferred = that._addFinishedDeferreds();
                    $.when.apply($, that._getFinishedDeferreds())
                        .done(function () {
                            that._trigger('stopped', e);
                        });
                    that._transition($(this).find('.fileupload-progress')).done(
                        function () {
                            $(this).find('.progress')
                                .attr('aria-valuenow', '0')
                                .children().first().css('width', '0%');
                            $(this).find('.progress-extended').html(' ');
                            deferred.resolve();
                        }
                    );
                },
                processstart: function (e) {
                    if (e.isDefaultPrevented()) {
                        return false;
                    }
                    $(this).addClass('fileupload-processing');
                },
                processstop: function (e) {
                    if (e.isDefaultPrevented()) {
                        return false;
                    }
                    $(this).removeClass('fileupload-processing');
                },
                // Callback for file deletion:
                destroy: function (e, data) {
                    if (e.isDefaultPrevented()) {
                        return false;
                    }        
                    var that = $(this).data('blueimp-fileupload') ||
                            $(this).data('fileupload'),
                        removeNode = function () {
                            that._transition(data.context).done(
                                function () {
                                    $(this).remove();
                                    that._trigger('destroyed', e, data);
                                }
                            );
                        };
                    if (data.url) {
                        data.dataType = data.dataType || that.options.dataType;
                        $.ajax(data).done(removeNode).fail(function () {
                            that._trigger('destroyfailed', e, data);
                        });
                    } else {
                        removeNode();
                    }
                }
            },
    
            _resetFinishedDeferreds: function () {
                this._finishedUploads = [];
            },
    
            _addFinishedDeferreds: function (deferred) {
                if (!deferred) {
                    deferred = $.Deferred();
                }
                this._finishedUploads.push(deferred);
                return deferred;
            },
    
            _getFinishedDeferreds: function () {
                return this._finishedUploads;
            },
    
            // Link handler, that allows to download files
            // by drag & drop of the links to the desktop:
            _enableDragToDesktop: function () {
                var link = $(this),
                    url = link.prop('href'),
                    name = link.prop('download'),
                    type = 'application/octet-stream';
                link.bind('dragstart', function (e) {
                    try {
                        e.originalEvent.dataTransfer.setData(
                            'DownloadURL',
                            [type, name, url].join(':')
                        );
                    } catch (ignore) {}
                });
            },
    
            _formatFileSize: function (bytes) {
                if (typeof bytes !== 'number') {
                    return '';
                }
                if (bytes >= 1000000000) {
                    return (bytes / 1000000000).toFixed(2) + ' GB';
                }
                if (bytes >= 1000000) {
                    return (bytes / 1000000).toFixed(2) + ' MB';
                }
                return (bytes / 1000).toFixed(2) + ' KB';
            },
    
            _formatBitrate: function (bits) {
                if (typeof bits !== 'number') {
                    return '';
                }
                if (bits >= 1000000000) {
                    return (bits / 1000000000).toFixed(2) + ' Gbit/s';
                }
                if (bits >= 1000000) {
                    return (bits / 1000000).toFixed(2) + ' Mbit/s';
                }
                if (bits >= 1000) {
                    return (bits / 1000).toFixed(2) + ' kbit/s';
                }
                return bits.toFixed(2) + ' bit/s';
            },
    
            _formatTime: function (seconds) {
                var date = new Date(seconds * 1000),
                    days = Math.floor(seconds / 86400);
                days = days ? days + 'd ' : '';
                return days +
                    ('0' + date.getUTCHours()).slice(-2) + ':' +
                    ('0' + date.getUTCMinutes()).slice(-2) + ':' +
                    ('0' + date.getUTCSeconds()).slice(-2);
            },
    
            _formatPercentage: function (floatValue) {
                return (floatValue * 100).toFixed(2) + ' %';
            },
    
            _renderExtendedProgress: function (data) {
                return this._formatBitrate(data.bitrate) + ' | ' +
                    this._formatTime(
                        (data.total - data.loaded) * 8 / data.bitrate
                    ) + ' | ' +
                    this._formatPercentage(
                        data.loaded / data.total
                    ) + ' | ' +
                    this._formatFileSize(data.loaded) + ' / ' +
                    this._formatFileSize(data.total);
            },
    
            _renderTemplate: function (func, files) {
                if (!func) {
                    return $();
                }
                var result = func({
                    files: files,
                    formatFileSize: this._formatFileSize,
                    options: this.options
                });
                if (result instanceof $) {
                    return result;
                }
                return $(this.options.templatesContainer).html(result).children();
            },
    
            _renderPreviews: function (data) {         
                data.context.find('.preview').each(function (index, elm) {
                    $(elm).append(data.files[index].preview);
                });
            },
    
            _renderUpload: function (files) {
                return this._renderTemplate(
                    this.options.uploadTemplate,
                    files
                );
            },
    
            _renderDownload: function (files) {
                return this._renderTemplate(
                    this.options.downloadTemplate,
                    files
                ).find('a[download]').each(this._enableDragToDesktop).end();
            },
    
            _startHandler: function (e) {
                e.preventDefault();
                var button = $(e.currentTarget),
                    template = button.closest('.template-upload'),
                    data = template.data('data');
                button.prop('disabled', true);
                if (data && data.submit) {
                    data.submit();
                }
            },
    
            _cancelHandler: function (e) {
                e.preventDefault();
                var template = $(e.currentTarget)
                        .closest('.template-upload,.template-download'),
                    data = template.data('data') || {};
                data.context = data.context || template;
                if (data.abort) {
                    data.abort();
                } else {
                    data.errorThrown = 'abort';
                    this._trigger('fail', e, data);
                }
            },
    
            _deleteHandler: function (e) {
                e.preventDefault();
                var button = $(e.currentTarget);
                this._trigger('destroy', e, $.extend({
                    context: button.closest('.template-download'),
                    type: 'DELETE'
                }, button.data()));
            },
    
            _forceReflow: function (node) {
                return $.support.transition && node.length &&
                    node[0].offsetWidth;
            },
    
            _transition: function (node) {
                var dfd = $.Deferred();
                if ($.support.transition && node.hasClass('fade') && node.is(':visible')) {
                    node.bind(
                        $.support.transition.end,
                        function (e) {
                            // Make sure we don't respond to other transitions events
                            // in the container element, e.g. from button elements:
                            if (e.target === node[0]) {
                                node.unbind($.support.transition.end);
                                dfd.resolveWith(node);
                            }
                        }
                    ).toggleClass('in');
                } else {
                    node.toggleClass('in');
                    dfd.resolveWith(node);
                }
                return dfd;
            },
    
            _initButtonBarEventHandlers: function () {
                var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
                    filesList = this.options.filesContainer;
                this._on(fileUploadButtonBar.find('.start'), {
                    click: function (e) {
                        e.preventDefault();
                        filesList.find('.start').click();
                    }
                });
                this._on(fileUploadButtonBar.find('.cancel'), {
                    click: function (e) {
                        e.preventDefault();
                        filesList.find('.cancel').click();
                    }
                });
                this._on(fileUploadButtonBar.find('.delete'), {
                    click: function (e) {
                        e.preventDefault();
                        filesList.find('.toggle:checked')
                            .closest('.template-download')
                            .find('.delete').click();
                        fileUploadButtonBar.find('.toggle')
                            .prop('checked', false);
                    }
                });
                this._on(fileUploadButtonBar.find('.toggle'), {
                    change: function (e) {
                        filesList.find('.toggle').prop(
                            'checked',
                            $(e.currentTarget).is(':checked')
                        );
                    }
                });
            },
    
            _destroyButtonBarEventHandlers: function () {
                this._off(
                    this.element.find('.fileupload-buttonbar')
                        .find('.start, .cancel, .delete'),
                    'click'
                );
                this._off(
                    this.element.find('.fileupload-buttonbar .toggle'),
                    'change.'
                );
            },
    
            _initEventHandlers: function () {
                this._super();
                this._on(this.options.filesContainer, {
                    'click .start': this._startHandler,
                    'click .cancel': this._cancelHandler,
                    'click .delete': this._deleteHandler
                });
                this._initButtonBarEventHandlers();
            },
    
            _destroyEventHandlers: function () {
                this._destroyButtonBarEventHandlers();
                this._off(this.options.filesContainer, 'click');
                this._super();
            },
    
            _enableFileInputButton: function () {
                this.element.find('.fileinput-button input')
                    .prop('disabled', false)
                    .parent().removeClass('disabled');
            },
    
            _disableFileInputButton: function () {
                this.element.find('.fileinput-button input')
                    .prop('disabled', true)
                    .parent().addClass('disabled');
            },
    
            _initTemplates: function () {
                var options = this.options;
                options.templatesContainer = this.document[0].createElement(
                    options.filesContainer.prop('nodeName')
                );
                if (tmpl) {
                    if (options.uploadTemplateId) {
                        options.uploadTemplate = tmpl(options.uploadTemplateId);
                    }
                    if (options.downloadTemplateId) {
                        options.downloadTemplate = tmpl(options.downloadTemplateId);
                    }
                }
            },
    
            _initFilesContainer: function () {
                var options = this.options;
                if (options.filesContainer === undefined) {
                    options.filesContainer = this.element.find('.files');
                } else if (!(options.filesContainer instanceof $)) {
                    options.filesContainer = $(options.filesContainer);
                }
            },
    
            _initSpecialOptions: function () {
                this._super();
                this._initFilesContainer();
                this._initTemplates();
            },
    
            _create: function () {
                this._super();
                this._resetFinishedDeferreds();
                if (!$.support.fileInput) {
                    this._disableFileInputButton();
                }
            },
    
            enable: function () {
                var wasDisabled = false;
                if (this.options.disabled) {
                    wasDisabled = true;
                }
                this._super();
                if (wasDisabled) {
                    this.element.find('input, button').prop('disabled', false);
                    this._enableFileInputButton();
                }
            },
    
            disable: function () {
                if (!this.options.disabled) {
                    this.element.find('input, button').prop('disabled', true);
                    this._disableFileInputButton();
                }
                this._super();
            }
    
        });
    
    }));
    

      

    ./js/main.js

    注释以下代码

        $('.fileupload').fileupload({
            // Uncomment the following to send cross-domain cookies:
            //xhrFields: {withCredentials: true},
            url: 'server/php/'
        });
    

    移至 ./index.html 中

    ./server/php/index.php

    <?php
    /*
     * jQuery File Upload Plugin PHP Example 5.14
     * https://github.com/blueimp/jQuery-File-Upload
     *
     * Copyright 2010, Sebastian Tschan
     * https://blueimp.net
     *
     * Licensed under the MIT license:
     * http://www.opensource.org/licenses/MIT
     */
    
    error_reporting(E_ALL | E_STRICT);
    require('UploadHandler.php');
    
    // 添加图片
    if(! empty($_FILES)) {
    	$keys = array_keys($_FILES);
    	if(! empty($keys)) {
    		$key = $keys[0];
    	} else {
    		$key = null;
    	}
    } else {
    	// 删除图片时的参数
    	$pathinfo = pathinfo($_SERVER['REQUEST_URI']);
    	if(! empty($pathinfo['filename'])) {
    		$dirname = preg_match('/^?(.*)=.*$/', $pathinfo['filename'], $match);
    		if(! empty($match[1]) && $match[1] != 'fields') {
    			$key = $match[1];
    		} else {
    			$key = null;
    		}
    	}	else {
    		$key = null;
    	}
    }
    
    $upload_handler = new UploadHandler($key);
    

    根据不同的添加/删除的不同情况,传递不同的参数,同时在页面加载时通过 ajax 获取不同文件夹的文件用于展示(并没有做多层目录或者文件存储目录与文件域的 name 不同的情况的考虑,同时实际项目的 url 模式也有可能与程序中的正则表达式不匹配,可在实际项目中根据实际情况修改)

    ./server/php/UploadHandler.php 上传类

    修改了 $this->options 中关于上传路径的参数,原 demo 中是写死的

        function __construct($dir = null, $options = null, $initialize = true, $error_messages = null) {
            if($dir === null) {
                $dir = 'files';
            }
            $this->options = array(
                'script_url' => $this->get_full_url().'/',
                'upload_dir' => dirname($this->get_server_var('SCRIPT_FILENAME')).'/'.$dir.'/',
                'upload_url' => $this->get_full_url().'/'.$dir.'/',
                'user_dirs' => false,
                'mkdir_mode' => 0755,
                'param_name' => $dir,
                ..........
    

      

    修改了 get_singular_param_name 方法

        protected function get_singular_param_name() {
            // return substr($this->options['param_name'], 0, -1);
            return $this->options['param_name'];
        }
    

     

    修改了 get 方法

        public function get($print_response = true) {
            if ($print_response && isset($_GET['download'])) {
                return $this->download();
            }
    
            if(isset($_GET['fields']) && $_GET['fields'] != '') {
                $fields = $_GET['fields'];
                if(strpos($fields, ',') === false) { // 只有一个实例
                    $this->options['upload_dir'] = dirname($this->get_server_var('SCRIPT_FILENAME')).'/'.$fields.'/';
                    $this->options['upload_url'] = $this->get_full_url().'/'.$fields.'/';
                    $this->options['param_name'] = $fields;
    
                    $response = array(
                        $val => $this->get_file_objects()
                    );
                    $return = $this->generate_response($response, $print_response);
                } else { // 多个实例
                    $fields = explode(',', $fields);
                    $return = array();
                    foreach($fields as $key => $val) {
    
                        $this->options['upload_dir'] = dirname($this->get_server_var('SCRIPT_FILENAME')).'/'.$val.'/';
                        $this->options['upload_url'] = $this->get_full_url().'/'.$val.'/';
                        $this->options['param_name'] = $val;
    
                        $response = $this->get_file_objects();
                        if(! empty($response)) {
                            $return[$val] = $response;
                        }
                    } 
                }
                echo json_encode($return);
            }
        }
    

    get 方法主要用于刷新页面后展示之前的上传列表,这个只是最简单的实现,实际项目中应该是通过数据库查找

    完整文件:

    <?php
    /*
     * jQuery File Upload Plugin PHP Class 8.1.0
     * https://github.com/blueimp/jQuery-File-Upload
     *
     * Copyright 2010, Sebastian Tschan
     * https://blueimp.net
     *
     * Licensed under the MIT license:
     * http://www.opensource.org/licenses/MIT
     */
    
    class UploadHandler
    {
    
        protected $options;
    
        // PHP File Upload error message codes:
        // http://php.net/manual/en/features.file-upload.errors.php
        protected $error_messages = array(
            1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
            2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
            3 => 'The uploaded file was only partially uploaded',
            4 => 'No file was uploaded',
            6 => 'Missing a temporary folder',
            7 => 'Failed to write file to disk',
            8 => 'A PHP extension stopped the file upload',
            'post_max_size' => 'The uploaded file exceeds the post_max_size directive in php.ini',
            'max_file_size' => 'File is too big',
            'min_file_size' => 'File is too small',
            'accept_file_types' => 'Filetype not allowed',
            'max_number_of_files' => 'Maximum number of files exceeded',
            'max_width' => 'Image exceeds maximum width',
            'min_width' => 'Image requires a minimum width',
            'max_height' => 'Image exceeds maximum height',
            'min_height' => 'Image requires a minimum height',
            'abort' => 'File upload aborted',
            'image_resize' => 'Failed to resize image'
        );
    
        protected $image_objects = array();
    
        function __construct($dir = null, $options = null, $initialize = true, $error_messages = null) {
            if($dir === null) {
                $dir = 'files';
            }
            $this->options = array(
                'script_url' => $this->get_full_url().'/',
                'upload_dir' => dirname($this->get_server_var('SCRIPT_FILENAME')).'/'.$dir.'/',
                'upload_url' => $this->get_full_url().'/'.$dir.'/',
                'user_dirs' => false,
                'mkdir_mode' => 0755,
                'param_name' => $dir,
                // Set the following option to 'POST', if your server does not support
                // DELETE requests. This is a parameter sent to the client:
                'delete_type' => 'DELETE',
                'access_control_allow_origin' => '*',
                'access_control_allow_credentials' => false,
                'access_control_allow_methods' => array(
                    'OPTIONS',
                    'HEAD',
                    'GET',
                    'POST',
                    'PUT',
                    'PATCH',
                    'DELETE'
                ),
                'access_control_allow_headers' => array(
                    'Content-Type',
                    'Content-Range',
                    'Content-Disposition'
                ),
                // Enable to provide file downloads via GET requests to the PHP script:
                //     1. Set to 1 to download files via readfile method through PHP
                //     2. Set to 2 to send a X-Sendfile header for lighttpd/Apache
                //     3. Set to 3 to send a X-Accel-Redirect header for nginx
                // If set to 2 or 3, adjust the upload_url option to the base path of
                // the redirect parameter, e.g. '/files/'.
                'download_via_php' => false,
                // Read files in chunks to avoid memory limits when download_via_php
                // is enabled, set to 0 to disable chunked reading of files:
                'readfile_chunk_size' => 10 * 1024 * 1024, // 10 MiB
                // Defines which files can be displayed inline when downloaded:
                'inline_file_types' => '/.(gif|jpe?g|png)$/i',
                // Defines which files (based on their names) are accepted for upload:
                'accept_file_types' => '/.+$/i',
                // The php.ini settings upload_max_filesize and post_max_size
                // take precedence over the following max_file_size setting:
                'max_file_size' => null,
                'min_file_size' => 1,
                // The maximum number of files for the upload directory:
                'max_number_of_files' => null,
                // Defines which files are handled as image files:
                'image_file_types' => '/.(gif|jpe?g|png)$/i',
                // Use exif_imagetype on all files to correct file extensions:
                'correct_image_extensions' => false,
                // Image resolution restrictions:
                'max_width' => null,
                'max_height' => null,
                'min_width' => 1,
                'min_height' => 1,
                // Set the following option to false to enable resumable uploads:
                'discard_aborted_uploads' => true,
                // Set to 0 to use the GD library to scale and orient images,
                // set to 1 to use imagick (if installed, falls back to GD),
                // set to 2 to use the ImageMagick convert binary directly:
                'image_library' => 1,
                // Uncomment the following to define an array of resource limits
                // for imagick:
                /*
                'imagick_resource_limits' => array(
                    imagick::RESOURCETYPE_MAP => 32,
                    imagick::RESOURCETYPE_MEMORY => 32
                ),
                */
                // Command or path for to the ImageMagick convert binary:
                'convert_bin' => 'convert',
                // Uncomment the following to add parameters in front of each
                // ImageMagick convert call (the limit constraints seem only
                // to have an effect if put in front):
                /*
                'convert_params' => '-limit memory 32MiB -limit map 32MiB',
                */
                // Command or path for to the ImageMagick identify binary:
                'identify_bin' => 'identify',
                'image_versions' => array(
                    // The empty image version key defines options for the original image:
                    '' => array(
                        // Automatically rotate images based on EXIF meta data:
                        'auto_orient' => true
                    ),
                    // Uncomment the following to create medium sized images:
                    /*
                    'medium' => array(
                        'max_width' => 800,
                        'max_height' => 600
                    ),
                    */
                    'thumbnail' => array(
                        // Uncomment the following to use a defined directory for the thumbnails
                        // instead of a subdirectory based on the version identifier.
                        // Make sure that this directory doesn't allow execution of files if you
                        // don't pose any restrictions on the type of uploaded files, e.g. by
                        // copying the .htaccess file from the files directory for Apache:
                        //'upload_dir' => dirname($this->get_server_var('SCRIPT_FILENAME')).'/thumb/',
                        //'upload_url' => $this->get_full_url().'/thumb/',
                        // Uncomment the following to force the max
                        // dimensions and e.g. create square thumbnails:
                        //'crop' => true,
                        'max_width' => 80,
                        'max_height' => 80
                    )
                )
            );
    
            if ($options) {
                $this->options = $options + $this->options;
            }
            if ($error_messages) {
                $this->error_messages = $error_messages + $this->error_messages;
            }
            if ($initialize) {
                $this->initialize();
            }
        }
    
        protected function initialize() {
            switch ($this->get_server_var('REQUEST_METHOD')) {
                case 'OPTIONS':
                case 'HEAD':
                    $this->head();
                    break;
                case 'GET':
                    $this->get();
                    break;
                case 'PATCH':
                case 'PUT':
                case 'POST':
                    $this->post();
                    break;
                case 'DELETE':
                    $this->delete();
                    break;
                default:
                    $this->header('HTTP/1.1 405 Method Not Allowed');
            }
        }
    
        protected function get_full_url() {
            $https = !empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'on') === 0 ||
                !empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
                    strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0;
            return
                ($https ? 'https://' : 'http://').
                (!empty($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'].'@' : '').
                (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME'].
                ($https && $_SERVER['SERVER_PORT'] === 443 ||
                $_SERVER['SERVER_PORT'] === 80 ? '' : ':'.$_SERVER['SERVER_PORT']))).
                substr($_SERVER['SCRIPT_NAME'],0, strrpos($_SERVER['SCRIPT_NAME'], '/'));
        }
    
        protected function get_user_id() {
            @session_start();
            return session_id();
        }
    
        protected function get_user_path() {
            if ($this->options['user_dirs']) {
                return $this->get_user_id().'/';
            }
            return '';
        }
    
        protected function get_upload_path($file_name = null, $version = null) {
            $file_name = $file_name ? $file_name : '';
            if (empty($version)) {
                $version_path = '';
            } else {
                $version_dir = @$this->options['image_versions'][$version]['upload_dir'];
                if ($version_dir) {
                    return $version_dir.$this->get_user_path().$file_name;
                }
                $version_path = $version.'/';
            }
            return $this->options['upload_dir'].$this->get_user_path()
                .$version_path.$file_name;
        }
    
        protected function get_query_separator($url) {
            return strpos($url, '?') === false ? '?' : '&';
        }
    
        protected function get_download_url($file_name, $version = null, $direct = false) {
            if (!$direct && $this->options['download_via_php']) {
                $url = $this->options['script_url']
                    .$this->get_query_separator($this->options['script_url'])
                    .$this->get_singular_param_name()
                    .'='.rawurlencode($file_name);
                if ($version) {
                    $url .= '&version='.rawurlencode($version);
                }
                return $url.'&download=1';
            }
            if (empty($version)) {
                $version_path = '';
            } else {
                $version_url = @$this->options['image_versions'][$version]['upload_url'];
                if ($version_url) {
                    return $version_url.$this->get_user_path().rawurlencode($file_name);
                }
                $version_path = rawurlencode($version).'/';
            }
            return $this->options['upload_url'].$this->get_user_path()
                .$version_path.rawurlencode($file_name);
        }
    
        protected function set_additional_file_properties($file) {
            $file->deleteUrl = $this->options['script_url']
                .$this->get_query_separator($this->options['script_url'])
                .$this->get_singular_param_name()
                .'='.rawurlencode($file->name);
            $file->deleteType = $this->options['delete_type'];
            if ($file->deleteType !== 'DELETE') {
                $file->deleteUrl .= '&_method=DELETE';
            }
            if ($this->options['access_control_allow_credentials']) {
                $file->deleteWithCredentials = true;
            }
        }
    
        // Fix for overflowing signed 32 bit integers,
        // works for sizes up to 2^32-1 bytes (4 GiB - 1):
        protected function fix_integer_overflow($size) {
            if ($size < 0) {
                $size += 2.0 * (PHP_INT_MAX + 1);
            }
            return $size;
        }
    
        protected function get_file_size($file_path, $clear_stat_cache = false) {
            if ($clear_stat_cache) {
                if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
                    clearstatcache(true, $file_path);
                } else {
                    clearstatcache();
                }
            }
            return $this->fix_integer_overflow(filesize($file_path));
        }
    
        protected function is_valid_file_object($file_name) {
            $file_path = $this->get_upload_path($file_name);
            if (is_file($file_path) && $file_name[0] !== '.') {
                return true;
            }
            return false;
        }
    
        protected function get_file_object($file_name) {
            if ($this->is_valid_file_object($file_name)) {
                $file = new stdClass();
                $file->name = $file_name;
                $file->size = $this->get_file_size(
                    $this->get_upload_path($file_name)
                );
                $file->url = $this->get_download_url($file->name);
                foreach($this->options['image_versions'] as $version => $options) {
                    if (!empty($version)) {
                        if (is_file($this->get_upload_path($file_name, $version))) {
                            $file->{$version.'Url'} = $this->get_download_url(
                                $file->name,
                                $version
                            );
                        }
                    }
                }
                $this->set_additional_file_properties($file);
                return $file;
            }
            return null;
        }
    
        protected function get_file_objects($iteration_method = 'get_file_object') {
            $upload_dir = $this->get_upload_path();
            if (!is_dir($upload_dir)) {
                return array();
            }
            return array_values(array_filter(array_map(
                array($this, $iteration_method),
                scandir($upload_dir)
            )));
        }
    
        protected function count_file_objects() {
            return count($this->get_file_objects('is_valid_file_object'));
        }
    
        protected function get_error_message($error) {
            return array_key_exists($error, $this->error_messages) ?
                $this->error_messages[$error] : $error;
        }
    
        function get_config_bytes($val) {
            $val = trim($val);
            $last = strtolower($val[strlen($val)-1]);
            switch($last) {
                case 'g':
                    $val *= 1024;
                case 'm':
                    $val *= 1024;
                case 'k':
                    $val *= 1024;
            }
            return $this->fix_integer_overflow($val);
        }
    
        protected function validate($uploaded_file, $file, $error, $index) {
            if ($error) {
                $file->error = $this->get_error_message($error);
                return false;
            }
            $content_length = $this->fix_integer_overflow(intval(
                $this->get_server_var('CONTENT_LENGTH')
            ));
            $post_max_size = $this->get_config_bytes(ini_get('post_max_size'));
            if ($post_max_size && ($content_length > $post_max_size)) {
                $file->error = $this->get_error_message('post_max_size');
                return false;
            }
            if (!preg_match($this->options['accept_file_types'], $file->name)) {
                $file->error = $this->get_error_message('accept_file_types');
                return false;
            }
            if ($uploaded_file && is_uploaded_file($uploaded_file)) {
                $file_size = $this->get_file_size($uploaded_file);
            } else {
                $file_size = $content_length;
            }
            if ($this->options['max_file_size'] && (
                    $file_size > $this->options['max_file_size'] ||
                    $file->size > $this->options['max_file_size'])
                ) {
                $file->error = $this->get_error_message('max_file_size');
                return false;
            }
            if ($this->options['min_file_size'] &&
                $file_size < $this->options['min_file_size']) {
                $file->error = $this->get_error_message('min_file_size');
                return false;
            }
            if (is_int($this->options['max_number_of_files']) &&
                    ($this->count_file_objects() >= $this->options['max_number_of_files']) &&
                    // Ignore additional chunks of existing files:
                    !is_file($this->get_upload_path($file->name))) {
                $file->error = $this->get_error_message('max_number_of_files');
                return false;
            }
            $max_width = @$this->options['max_width'];
            $max_height = @$this->options['max_height'];
            $min_width = @$this->options['min_width'];
            $min_height = @$this->options['min_height'];
            if (($max_width || $max_height || $min_width || $min_height)
               && preg_match($this->options['image_file_types'], $file->name)) {
                list($img_width, $img_height) = $this->get_image_size($uploaded_file);
            }
            if (!empty($img_width)) {
                if ($max_width && $img_width > $max_width) {
                    $file->error = $this->get_error_message('max_width');
                    return false;
                }
                if ($max_height && $img_height > $max_height) {
                    $file->error = $this->get_error_message('max_height');
                    return false;
                }
                if ($min_width && $img_width < $min_width) {
                    $file->error = $this->get_error_message('min_width');
                    return false;
                }
                if ($min_height && $img_height < $min_height) {
                    $file->error = $this->get_error_message('min_height');
                    return false;
                }
            }
            return true;
        }
    
        protected function upcount_name_callback($matches) {
            $index = isset($matches[1]) ? intval($matches[1]) + 1 : 1;
            $ext = isset($matches[2]) ? $matches[2] : '';
            return ' ('.$index.')'.$ext;
        }
    
        protected function upcount_name($name) {
            return preg_replace_callback(
                '/(?:(?: (([d]+)))?(.[^.]+))?$/',
                array($this, 'upcount_name_callback'),
                $name,
                1
            );
        }
    
        protected function get_unique_filename($file_path, $name, $size, $type, $error,
                $index, $content_range) {
            while(is_dir($this->get_upload_path($name))) {
                $name = $this->upcount_name($name);
            }
            // Keep an existing filename if this is part of a chunked upload:
            $uploaded_bytes = $this->fix_integer_overflow(intval($content_range[1]));
            while(is_file($this->get_upload_path($name))) {
                if ($uploaded_bytes === $this->get_file_size(
                        $this->get_upload_path($name))) {
                    break;
                }
                $name = $this->upcount_name($name);
            }
            return $name;
        }
    
        protected function fix_file_extension($file_path, $name, $size, $type, $error,
                $index, $content_range) {
            // Add missing file extension for known image types:
            if (strpos($name, '.') === false &&
                    preg_match('/^image/(gif|jpe?g|png)/', $type, $matches)) {
                $name .= '.'.$matches[1];
            }
            if ($this->options['correct_image_extensions'] &&
                    function_exists('exif_imagetype')) {
                switch(@exif_imagetype($file_path)){
                    case IMAGETYPE_JPEG:
                        $extensions = array('jpg', 'jpeg');
                        break;
                    case IMAGETYPE_PNG:
                        $extensions = array('png');
                        break;
                    case IMAGETYPE_GIF:
                        $extensions = array('gif');
                        break;
                }
                // Adjust incorrect image file extensions:
                if (!empty($extensions)) {
                    $parts = explode('.', $name);
                    $extIndex = count($parts) - 1;
                    $ext = strtolower(@$parts[$extIndex]);
                    if (!in_array($ext, $extensions)) {
                        $parts[$extIndex] = $extensions[0];
                        $name = implode('.', $parts);
                    }
                }
            }
            return $name;
        }
    
        protected function trim_file_name($file_path, $name, $size, $type, $error,
                $index, $content_range) {
            // Remove path information and dots around the filename, to prevent uploading
            // into different directories or replacing hidden system files.
            // Also remove control characters and spaces (x00..x20) around the filename:
            $name = trim(basename(stripslashes($name)), ".x00..x20");
            // Use a timestamp for empty filenames:
            if (!$name) {
                $name = str_replace('.', '-', microtime(true));
            }
            return $name;
        }
    
        protected function get_file_name($file_path, $name, $size, $type, $error,
                $index, $content_range) {
            $name = $this->trim_file_name($file_path, $name, $size, $type, $error,
                $index, $content_range);
            return $this->get_unique_filename(
                $file_path,
                $this->fix_file_extension($file_path, $name, $size, $type, $error,
                    $index, $content_range),
                $size,
                $type,
                $error,
                $index,
                $content_range
            );
        }
    
        protected function handle_form_data($file, $index) {
            // Handle form data, e.g. $_REQUEST['description'][$index]
        }
    
        protected function get_scaled_image_file_paths($file_name, $version) {
            $file_path = $this->get_upload_path($file_name);
            if (!empty($version)) {
                $version_dir = $this->get_upload_path(null, $version);
                if (!is_dir($version_dir)) {
                    mkdir($version_dir, $this->options['mkdir_mode'], true);
                }
                $new_file_path = $version_dir.'/'.$file_name;
            } else {
                $new_file_path = $file_path;
            }
            return array($file_path, $new_file_path);
        }
    
        protected function gd_get_image_object($file_path, $func, $no_cache = false) {
            if (empty($this->image_objects[$file_path]) || $no_cache) {
                $this->gd_destroy_image_object($file_path);
                $this->image_objects[$file_path] = $func($file_path);
            }
            return $this->image_objects[$file_path];
        }
    
        protected function gd_set_image_object($file_path, $image) {
            $this->gd_destroy_image_object($file_path);
            $this->image_objects[$file_path] = $image;
        }
    
        protected function gd_destroy_image_object($file_path) {
            $image = (isset($this->image_objects[$file_path])) ? $this->image_objects[$file_path] : null ;
            return $image && imagedestroy($image);
        }
    
        protected function gd_imageflip($image, $mode) {
            if (function_exists('imageflip')) {
                return imageflip($image, $mode);
            }
            $new_width = $src_width = imagesx($image);
            $new_height = $src_height = imagesy($image);
            $new_img = imagecreatetruecolor($new_width, $new_height);
            $src_x = 0;
            $src_y = 0;
            switch ($mode) {
                case '1': // flip on the horizontal axis
                    $src_y = $new_height - 1;
                    $src_height = -$new_height;
                    break;
                case '2': // flip on the vertical axis
                    $src_x  = $new_width - 1;
                    $src_width = -$new_width;
                    break;
                case '3': // flip on both axes
                    $src_y = $new_height - 1;
                    $src_height = -$new_height;
                    $src_x  = $new_width - 1;
                    $src_width = -$new_width;
                    break;
                default:
                    return $image;
            }
            imagecopyresampled(
                $new_img,
                $image,
                0,
                0,
                $src_x,
                $src_y,
                $new_width,
                $new_height,
                $src_width,
                $src_height
            );
            return $new_img;
        }
    
        protected function gd_orient_image($file_path, $src_img) {
            if (!function_exists('exif_read_data')) {
                return false;
            }
            $exif = @exif_read_data($file_path);
            if ($exif === false) {
                return false;
            }
            $orientation = intval(@$exif['Orientation']);
            if ($orientation < 2 || $orientation > 8) {
                return false;
            }
            switch ($orientation) {
                case 2:
                    $new_img = $this->gd_imageflip(
                        $src_img,
                        defined('IMG_FLIP_VERTICAL') ? IMG_FLIP_VERTICAL : 2
                    );
                    break;
                case 3:
                    $new_img = imagerotate($src_img, 180, 0);
                    break;
                case 4:
                    $new_img = $this->gd_imageflip(
                        $src_img,
                        defined('IMG_FLIP_HORIZONTAL') ? IMG_FLIP_HORIZONTAL : 1
                    );
                    break;
                case 5:
                    $tmp_img = $this->gd_imageflip(
                        $src_img,
                        defined('IMG_FLIP_HORIZONTAL') ? IMG_FLIP_HORIZONTAL : 1
                    );
                    $new_img = imagerotate($tmp_img, 270, 0);
                    imagedestroy($tmp_img);
                    break;
                case 6:
                    $new_img = imagerotate($src_img, 270, 0);
                    break;
                case 7:
                    $tmp_img = $this->gd_imageflip(
                        $src_img,
                        defined('IMG_FLIP_VERTICAL') ? IMG_FLIP_VERTICAL : 2
                    );
                    $new_img = imagerotate($tmp_img, 270, 0);
                    imagedestroy($tmp_img);
                    break;
                case 8:
                    $new_img = imagerotate($src_img, 90, 0);
                    break;
                default:
                    return false;
            }
            $this->gd_set_image_object($file_path, $new_img);
            return true;
        }
    
        protected function gd_create_scaled_image($file_name, $version, $options) {
            if (!function_exists('imagecreatetruecolor')) {
                error_log('Function not found: imagecreatetruecolor');
                return false;
            }
            list($file_path, $new_file_path) =
                $this->get_scaled_image_file_paths($file_name, $version);
            $type = strtolower(substr(strrchr($file_name, '.'), 1));
            switch ($type) {
                case 'jpg':
                case 'jpeg':
                    $src_func = 'imagecreatefromjpeg';
                    $write_func = 'imagejpeg';
                    $image_quality = isset($options['jpeg_quality']) ?
                        $options['jpeg_quality'] : 75;
                    break;
                case 'gif':
                    $src_func = 'imagecreatefromgif';
                    $write_func = 'imagegif';
                    $image_quality = null;
                    break;
                case 'png':
                    $src_func = 'imagecreatefrompng';
                    $write_func = 'imagepng';
                    $image_quality = isset($options['png_quality']) ?
                        $options['png_quality'] : 9;
                    break;
                default:
                    return false;
            }
            $src_img = $this->gd_get_image_object(
                $file_path,
                $src_func,
                !empty($options['no_cache'])
            );
            $image_oriented = false;
            if (!empty($options['auto_orient']) && $this->gd_orient_image(
                    $file_path,
                    $src_img
                )) {
                $image_oriented = true;
                $src_img = $this->gd_get_image_object(
                    $file_path,
                    $src_func
                );
            }
            $max_width = $img_width = imagesx($src_img);
            $max_height = $img_height = imagesy($src_img);
            if (!empty($options['max_width'])) {
                $max_width = $options['max_width'];
            }
            if (!empty($options['max_height'])) {
                $max_height = $options['max_height'];
            }
            $scale = min(
                $max_width / $img_width,
                $max_height / $img_height
            );
            if ($scale >= 1) {
                if ($image_oriented) {
                    return $write_func($src_img, $new_file_path, $image_quality);
                }
                if ($file_path !== $new_file_path) {
                    return copy($file_path, $new_file_path);
                }
                return true;
            }
            if (empty($options['crop'])) {
                $new_width = $img_width * $scale;
                $new_height = $img_height * $scale;
                $dst_x = 0;
                $dst_y = 0;
                $new_img = imagecreatetruecolor($new_width, $new_height);
            } else {
                if (($img_width / $img_height) >= ($max_width / $max_height)) {
                    $new_width = $img_width / ($img_height / $max_height);
                    $new_height = $max_height;
                } else {
                    $new_width = $max_width;
                    $new_height = $img_height / ($img_width / $max_width);
                }
                $dst_x = 0 - ($new_width - $max_width) / 2;
                $dst_y = 0 - ($new_height - $max_height) / 2;
                $new_img = imagecreatetruecolor($max_width, $max_height);
            }
            // Handle transparency in GIF and PNG images:
            switch ($type) {
                case 'gif':
                case 'png':
                    imagecolortransparent($new_img, imagecolorallocate($new_img, 0, 0, 0));
                case 'png':
                    imagealphablending($new_img, false);
                    imagesavealpha($new_img, true);
                    break;
            }
            $success = imagecopyresampled(
                $new_img,
                $src_img,
                $dst_x,
                $dst_y,
                0,
                0,
                $new_width,
                $new_height,
                $img_width,
                $img_height
            ) && $write_func($new_img, $new_file_path, $image_quality);
            $this->gd_set_image_object($file_path, $new_img);
            return $success;
        }
    
        protected function imagick_get_image_object($file_path, $no_cache = false) {
            if (empty($this->image_objects[$file_path]) || $no_cache) {
                $this->imagick_destroy_image_object($file_path);
                $image = new Imagick();
                if (!empty($this->options['imagick_resource_limits'])) {
                    foreach ($this->options['imagick_resource_limits'] as $type => $limit) {
                        $image->setResourceLimit($type, $limit);
                    }
                }
                $image->readImage($file_path);
                $this->image_objects[$file_path] = $image;
            }
            return $this->image_objects[$file_path];
        }
    
        protected function imagick_set_image_object($file_path, $image) {
            $this->imagick_destroy_image_object($file_path);
            $this->image_objects[$file_path] = $image;
        }
    
        protected function imagick_destroy_image_object($file_path) {
            $image = (isset($this->image_objects[$file_path])) ? $this->image_objects[$file_path] : null ;
            return $image && $image->destroy();
        }
    
        protected function imagick_orient_image($image) {
            $orientation = $image->getImageOrientation();
            $background = new ImagickPixel('none');
            switch ($orientation) {
                case imagick::ORIENTATION_TOPRIGHT: // 2
                    $image->flopImage(); // horizontal flop around y-axis
                    break;
                case imagick::ORIENTATION_BOTTOMRIGHT: // 3
                    $image->rotateImage($background, 180);
                    break;
                case imagick::ORIENTATION_BOTTOMLEFT: // 4
                    $image->flipImage(); // vertical flip around x-axis
                    break;
                case imagick::ORIENTATION_LEFTTOP: // 5
                    $image->flopImage(); // horizontal flop around y-axis
                    $image->rotateImage($background, 270);
                    break;
                case imagick::ORIENTATION_RIGHTTOP: // 6
                    $image->rotateImage($background, 90);
                    break;
                case imagick::ORIENTATION_RIGHTBOTTOM: // 7
                    $image->flipImage(); // vertical flip around x-axis
                    $image->rotateImage($background, 270);
                    break;
                case imagick::ORIENTATION_LEFTBOTTOM: // 8
                    $image->rotateImage($background, 270);
                    break;
                default:
                    return false;
            }
            $image->setImageOrientation(imagick::ORIENTATION_TOPLEFT); // 1
            return true;
        }
    
        protected function imagick_create_scaled_image($file_name, $version, $options) {
            list($file_path, $new_file_path) =
                $this->get_scaled_image_file_paths($file_name, $version);
            $image = $this->imagick_get_image_object(
                $file_path,
                !empty($options['no_cache'])
            );
            if ($image->getImageFormat() === 'GIF') {
                // Handle animated GIFs:
                $images = $image->coalesceImages();
                foreach ($images as $frame) {
                    $image = $frame;
                    $this->imagick_set_image_object($file_name, $image);
                    break;
                }
            }
            $image_oriented = false;
            if (!empty($options['auto_orient'])) {
                $image_oriented = $this->imagick_orient_image($image);
            }
            $new_width = $max_width = $img_width = $image->getImageWidth();
            $new_height = $max_height = $img_height = $image->getImageHeight();
            if (!empty($options['max_width'])) {
                $new_width = $max_width = $options['max_width'];
            }
            if (!empty($options['max_height'])) {
                $new_height = $max_height = $options['max_height'];
            }
            if (!($image_oriented || $max_width < $img_width || $max_height < $img_height)) {
                if ($file_path !== $new_file_path) {
                    return copy($file_path, $new_file_path);
                }
                return true;
            }
            $crop = !empty($options['crop']);
            if ($crop) {
                $x = 0;
                $y = 0;
                if (($img_width / $img_height) >= ($max_width / $max_height)) {
                    $new_width = 0; // Enables proportional scaling based on max_height
                    $x = ($img_width / ($img_height / $max_height) - $max_width) / 2;
                } else {
                    $new_height = 0; // Enables proportional scaling based on max_width
                    $y = ($img_height / ($img_width / $max_width) - $max_height) / 2;
                }
            }
            $success = $image->resizeImage(
                $new_width,
                $new_height,
                isset($options['filter']) ? $options['filter'] : imagick::FILTER_LANCZOS,
                isset($options['blur']) ? $options['blur'] : 1,
                $new_width && $new_height // fit image into constraints if not to be cropped
            );
            if ($success && $crop) {
                $success = $image->cropImage(
                    $max_width,
                    $max_height,
                    $x,
                    $y
                );
                if ($success) {
                    $success = $image->setImagePage($max_width, $max_height, 0, 0);
                }
            }
            $type = strtolower(substr(strrchr($file_name, '.'), 1));
            switch ($type) {
                case 'jpg':
                case 'jpeg':
                    if (!empty($options['jpeg_quality'])) {
                        $image->setImageCompression(imagick::COMPRESSION_JPEG);
                        $image->setImageCompressionQuality($options['jpeg_quality']);
                    }
                    break;
            }
            if (!empty($options['strip'])) {
                $image->stripImage();
            }
            return $success && $image->writeImage($new_file_path);
        }
    
        protected function imagemagick_create_scaled_image($file_name, $version, $options) {
            list($file_path, $new_file_path) =
                $this->get_scaled_image_file_paths($file_name, $version);
            $resize = @$options['max_width']
                .(empty($options['max_height']) ? '' : 'X'.$options['max_height']);
            if (!$resize && empty($options['auto_orient'])) {
                if ($file_path !== $new_file_path) {
                    return copy($file_path, $new_file_path);
                }
                return true;
            }
            $cmd = $this->options['convert_bin'];
            if (!empty($this->options['convert_params'])) {
                $cmd .= ' '.$this->options['convert_params'];
            }
            $cmd .= ' '.escapeshellarg($file_path);
            if (!empty($options['auto_orient'])) {
                $cmd .= ' -auto-orient';
            }
            if ($resize) {
                // Handle animated GIFs:
                $cmd .= ' -coalesce';
                if (empty($options['crop'])) {
                    $cmd .= ' -resize '.escapeshellarg($resize.'>');
                } else {
                    $cmd .= ' -resize '.escapeshellarg($resize.'^');
                    $cmd .= ' -gravity center';
                    $cmd .= ' -crop '.escapeshellarg($resize.'+0+0');
                }
                // Make sure the page dimensions are correct (fixes offsets of animated GIFs):
                $cmd .= ' +repage';
            }
            if (!empty($options['convert_params'])) {
                $cmd .= ' '.$options['convert_params'];
            }
            $cmd .= ' '.escapeshellarg($new_file_path);
            exec($cmd, $output, $error);
            if ($error) {
                error_log(implode('
    ', $output));
                return false;
            }
            return true;
        }
    
        protected function get_image_size($file_path) {
            if ($this->options['image_library']) {
                if (extension_loaded('imagick')) {
                    $image = new Imagick();
                    try {
                        if (@$image->pingImage($file_path)) {
                            $dimensions = array($image->getImageWidth(), $image->getImageHeight());
                            $image->destroy();
                            return $dimensions;
                        }
                        return false;
                    } catch (Exception $e) {
                        error_log($e->getMessage());
                    }
                }
                if ($this->options['image_library'] === 2) {
                    $cmd = $this->options['identify_bin'];
                    $cmd .= ' -ping '.escapeshellarg($file_path);
                    exec($cmd, $output, $error);
                    if (!$error && !empty($output)) {
                        // image.jpg JPEG 1920x1080 1920x1080+0+0 8-bit sRGB 465KB 0.000u 0:00.000
                        $infos = preg_split('/s+/', $output[0]);
                        $dimensions = preg_split('/x/', $infos[2]);
                        return $dimensions;
                    }
                    return false;
                }
            }
            if (!function_exists('getimagesize')) {
                error_log('Function not found: getimagesize');
                return false;
            }
            return @getimagesize($file_path);
        }
    
        protected function create_scaled_image($file_name, $version, $options) {
            if ($this->options['image_library'] === 2) {
                return $this->imagemagick_create_scaled_image($file_name, $version, $options);
            }
            if ($this->options['image_library'] && extension_loaded('imagick')) {
                return $this->imagick_create_scaled_image($file_name, $version, $options);
            }
            return $this->gd_create_scaled_image($file_name, $version, $options);
        }
    
        protected function destroy_image_object($file_path) {
            if ($this->options['image_library'] && extension_loaded('imagick')) {
                return $this->imagick_destroy_image_object($file_path);
            }
        }
    
        protected function is_valid_image_file($file_path) {
            if (!preg_match($this->options['image_file_types'], $file_path)) {
                return false;
            }
            if (function_exists('exif_imagetype')) {
                return @exif_imagetype($file_path);
            }
            $image_info = $this->get_image_size($file_path);
            return $image_info && $image_info[0] && $image_info[1];
        }
    
        protected function handle_image_file($file_path, $file) {
            $failed_versions = array();
            foreach($this->options['image_versions'] as $version => $options) {
                if ($this->create_scaled_image($file->name, $version, $options)) {
                    if (!empty($version)) {
                        $file->{$version.'Url'} = $this->get_download_url(
                            $file->name,
                            $version
                        );
                    } else {
                        $file->size = $this->get_file_size($file_path, true);
                    }
                } else {
                    $failed_versions[] = $version ? $version : 'original';
                }
            }
            if (count($failed_versions)) {
                $file->error = $this->get_error_message('image_resize')
                        .' ('.implode($failed_versions,', ').')';
            }
            // Free memory:
            $this->destroy_image_object($file_path);
        }
    
        protected function handle_file_upload($uploaded_file, $name, $size, $type, $error,
                $index = null, $content_range = null) {
            $file = new stdClass();
            $file->name = $this->get_file_name($uploaded_file, $name, $size, $type, $error,
                $index, $content_range);
            $file->size = $this->fix_integer_overflow(intval($size));
            $file->type = $type;
            if ($this->validate($uploaded_file, $file, $error, $index)) {
                $this->handle_form_data($file, $index);
                $upload_dir = $this->get_upload_path();
                if (!is_dir($upload_dir)) {
                    mkdir($upload_dir, $this->options['mkdir_mode'], true);
                }
                $file_path = $this->get_upload_path($file->name);
                $append_file = $content_range && is_file($file_path) &&
                    $file->size > $this->get_file_size($file_path);
                if ($uploaded_file && is_uploaded_file($uploaded_file)) {
                    // multipart/formdata uploads (POST method uploads)
                    if ($append_file) {
                        file_put_contents(
                            $file_path,
                            fopen($uploaded_file, 'r'),
                            FILE_APPEND
                        );
                    } else {
                        move_uploaded_file($uploaded_file, $file_path);
                    }
                } else {
                    // Non-multipart uploads (PUT method support)
                    file_put_contents(
                        $file_path,
                        fopen('php://input', 'r'),
                        $append_file ? FILE_APPEND : 0
                    );
                }
                $file_size = $this->get_file_size($file_path, $append_file);
                if ($file_size === $file->size) {
                    $file->url = $this->get_download_url($file->name);
                    if ($this->is_valid_image_file($file_path)) {
                        $this->handle_image_file($file_path, $file);
                    }
                } else {
                    $file->size = $file_size;
                    if (!$content_range && $this->options['discard_aborted_uploads']) {
                        unlink($file_path);
                        $file->error = $this->get_error_message('abort');
                    }
                }
                $this->set_additional_file_properties($file);
            }
            return $file;
        }
    
        protected function readfile($file_path) {
            $file_size = $this->get_file_size($file_path);
            $chunk_size = $this->options['readfile_chunk_size'];
            if ($chunk_size && $file_size > $chunk_size) {
                $handle = fopen($file_path, 'rb');
                while (!feof($handle)) {
                    echo fread($handle, $chunk_size);
                    @ob_flush();
                    @flush();
                }
                fclose($handle);
                return $file_size;
            }
            return readfile($file_path);
        }
    
        protected function body($str) {
            echo $str;
        }
        
        protected function header($str) {
            header($str);
        }
    
        protected function get_server_var($id) {
            return isset($_SERVER[$id]) ? $_SERVER[$id] : '';
        }
    
        protected function generate_response($content, $print_response = true) {
            if ($print_response) {
                $json = json_encode($content);
                $redirect = isset($_REQUEST['redirect']) ?
                    stripslashes($_REQUEST['redirect']) : null;
                if ($redirect) {
                    $this->header('Location: '.sprintf($redirect, rawurlencode($json)));
                    return;
                }
                $this->head();
                if ($this->get_server_var('HTTP_CONTENT_RANGE')) {
                    $files = isset($content[$this->options['param_name']]) ?
                        $content[$this->options['param_name']] : null;
                    if ($files && is_array($files) && is_object($files[0]) && $files[0]->size) {
                        $this->header('Range: 0-'.(
                            $this->fix_integer_overflow(intval($files[0]->size)) - 1
                        ));
                    }
                }
                $this->body($json);
            }
            return $json;
        }
    
        protected function get_version_param() {
            return isset($_GET['version']) ? basename(stripslashes($_GET['version'])) : null;
        }
    
        protected function get_singular_param_name() {
            // return substr($this->options['param_name'], 0, -1);
            return $this->options['param_name'];
        }
    
        protected function get_file_name_param() {
            $name = $this->get_singular_param_name();
            return isset($_REQUEST[$name]) ? basename(stripslashes($_REQUEST[$name])) : null;
        }
    
        protected function get_file_names_params() {
            $params = isset($_REQUEST[$this->options['param_name']]) ?
                $_REQUEST[$this->options['param_name']] : array();
            foreach ($params as $key => $value) {
                $params[$key] = basename(stripslashes($value));
            }
            return $params;
        }
    
        protected function get_file_type($file_path) {
            switch (strtolower(pathinfo($file_path, PATHINFO_EXTENSION))) {
                case 'jpeg':
                case 'jpg':
                    return 'image/jpeg';
                case 'png':
                    return 'image/png';
                case 'gif':
                    return 'image/gif';
                default:
                    return '';
            }
        }
    
        protected function download() {
            switch ($this->options['download_via_php']) {
                case 1:
                    $redirect_header = null;
                    break;
                case 2:
                    $redirect_header = 'X-Sendfile';
                    break;
                case 3:
                    $redirect_header = 'X-Accel-Redirect';
                    break;
                default:
                    return $this->header('HTTP/1.1 403 Forbidden');
            }
            $file_name = $this->get_file_name_param();
            if (!$this->is_valid_file_object($file_name)) {
                return $this->header('HTTP/1.1 404 Not Found');
            }
            if ($redirect_header) {
                return $this->header(
                    $redirect_header.': '.$this->get_download_url(
                        $file_name,
                        $this->get_version_param(),
                        true
                    )
                );
            }
            $file_path = $this->get_upload_path($file_name, $this->get_version_param());
            // Prevent browsers from MIME-sniffing the content-type:
            $this->header('X-Content-Type-Options: nosniff');
            if (!preg_match($this->options['inline_file_types'], $file_name)) {
                $this->header('Content-Type: application/octet-stream');
                $this->header('Content-Disposition: attachment; filename="'.$file_name.'"');
            } else {
                $this->header('Content-Type: '.$this->get_file_type($file_path));
                $this->header('Content-Disposition: inline; filename="'.$file_name.'"');
            }
            $this->header('Content-Length: '.$this->get_file_size($file_path));
            $this->header('Last-Modified: '.gmdate('D, d M Y H:i:s T', filemtime($file_path)));
            $this->readfile($file_path);
        }
    
        protected function send_content_type_header() {
            $this->header('Vary: Accept');
            if (strpos($this->get_server_var('HTTP_ACCEPT'), 'application/json') !== false) {
                $this->header('Content-type: application/json');
            } else {
                $this->header('Content-type: text/plain');
            }
        }
    
        protected function send_access_control_headers() {
            $this->header('Access-Control-Allow-Origin: '.$this->options['access_control_allow_origin']);
            $this->header('Access-Control-Allow-Credentials: '
                .($this->options['access_control_allow_credentials'] ? 'true' : 'false'));
            $this->header('Access-Control-Allow-Methods: '
                .implode(', ', $this->options['access_control_allow_methods']));
            $this->header('Access-Control-Allow-Headers: '
                .implode(', ', $this->options['access_control_allow_headers']));
        }
    
        public function head() {
            $this->header('Pragma: no-cache');
            $this->header('Cache-Control: no-store, no-cache, must-revalidate');
            $this->header('Content-Disposition: inline; filename="files.json"');
            // Prevent Internet Explorer from MIME-sniffing the content-type:
            $this->header('X-Content-Type-Options: nosniff');
            if ($this->options['access_control_allow_origin']) {
                $this->send_access_control_headers();
            }
            $this->send_content_type_header();
        }
    
        public function get($print_response = true) {
            if ($print_response && isset($_GET['download'])) {
                return $this->download();
            }
            // $file_name = $this->get_file_name_param();
            // if ($file_name) {
            //     $response = array(
            //         $this->get_singular_param_name() => $this->get_file_object($file_name)
            //     );
            // } else {
            //     $response = array(
            //         $this->options['param_name'] => $this->get_file_objects()
            //     );
            // }
            // return $this->generate_response($response, $print_response);
    
            //print_r($this->options);
            if(isset($_GET['fields']) && $_GET['fields'] != '') {
                $fields = $_GET['fields'];
                if(strpos($fields, ',') === false) { // 只有一个实例
                    $this->options['upload_dir'] = dirname($this->get_server_var('SCRIPT_FILENAME')).'/'.$fields.'/';
                    $this->options['upload_url'] = $this->get_full_url().'/'.$fields.'/';
                    $this->options['param_name'] = $fields;
    
                    $response = array(
                        $val => $this->get_file_objects()
                    );
                    $return = $this->generate_response($response, $print_response);
                } else { // 多个实例
                    $fields = explode(',', $fields);
                    $return = array();
                    foreach($fields as $key => $val) {
    
                        $this->options['upload_dir'] = dirname($this->get_server_var('SCRIPT_FILENAME')).'/'.$val.'/';
                        $this->options['upload_url'] = $this->get_full_url().'/'.$val.'/';
                        $this->options['param_name'] = $val;
    
                        $response = $this->get_file_objects();
                        if(! empty($response)) {
                            $return[$val] = $response;
                        }
                    } 
                }
                echo json_encode($return);
            }
        }
    
        public function post($print_response = true) {
            if (isset($_REQUEST['_method']) && $_REQUEST['_method'] === 'DELETE') {
                return $this->delete($print_response);
            }
            $upload = isset($_FILES[$this->options['param_name']]) ?
                $_FILES[$this->options['param_name']] : null;
            // Parse the Content-Disposition header, if available:
            $file_name = $this->get_server_var('HTTP_CONTENT_DISPOSITION') ?
                rawurldecode(preg_replace(
                    '/(^[^"]+")|("$)/',
                    '',
                    $this->get_server_var('HTTP_CONTENT_DISPOSITION')
                )) : null;
            // Parse the Content-Range header, which has the following form:
            // Content-Range: bytes 0-524287/2000000
            $content_range = $this->get_server_var('HTTP_CONTENT_RANGE') ?
                preg_split('/[^0-9]+/', $this->get_server_var('HTTP_CONTENT_RANGE')) : null;
            $size =  $content_range ? $content_range[3] : null;
            $files = array();
            if ($upload && is_array($upload['tmp_name'])) {
                // param_name is an array identifier like "files[]",
                // $_FILES is a multi-dimensional array:
                foreach ($upload['tmp_name'] as $index => $value) {
                    $files[] = $this->handle_file_upload(
                        $upload['tmp_name'][$index],
                        $file_name ? $file_name : $upload['name'][$index],
                        $size ? $size : $upload['size'][$index],
                        $upload['type'][$index],
                        $upload['error'][$index],
                        $index,
                        $content_range
                    );
                }
            } else {
                // param_name is a single object identifier like "file",
                // $_FILES is a one-dimensional array:
                $files[] = $this->handle_file_upload(
                    isset($upload['tmp_name']) ? $upload['tmp_name'] : null,
                    $file_name ? $file_name : (isset($upload['name']) ?
                            $upload['name'] : null),
                    $size ? $size : (isset($upload['size']) ?
                            $upload['size'] : $this->get_server_var('CONTENT_LENGTH')),
                    isset($upload['type']) ?
                            $upload['type'] : $this->get_server_var('CONTENT_TYPE'),
                    isset($upload['error']) ? $upload['error'] : null,
                    null,
                    $content_range
                );
            }
            return $this->generate_response(
                array($this->options['param_name'] => $files),
                $print_response
            );
        }
    
        public function delete($print_response = true) {
            // $file_names = $this->get_file_names_params();
            if (empty($file_names)) {
                $file_names = array($this->get_file_name_param());
            }
            $response = array();
            foreach($file_names as $file_name) {
                $file_path = $this->get_upload_path($file_name);
                $success = is_file($file_path) && $file_name[0] !== '.' && unlink($file_path);
                if ($success) {
                    foreach($this->options['image_versions'] as $version => $options) {
                        if (!empty($version)) {
                            $file = $this->get_upload_path($file_name, $version);
                            if (is_file($file)) {
                                unlink($file);
                            }
                        }
                    }
                }
                $response[$file_name] = $success;
            }
            return $this->generate_response($response, $print_response);
        }
    
    }
    

      

     GitHub 地址: https://github.com/dee0912/jquery_file_upload_multipart

  • 相关阅读:
    点击新闻弹出新闻内容,并且新闻内容根据鼠标移动
    阻止用户复制页面上的文字的几种方法
    js简易聊天框- 鼠标和回车键发送消息
    Java——package和import关键字
    Java——static关键字
    Java——单例模式初步
    Java——this关键字
    Java面向对象的特征一:封装性
    Java再谈方法
    Java——对象
  • 原文地址:https://www.cnblogs.com/dee0912/p/5592619.html
Copyright © 2020-2023  润新知