Pull to refresh

jQuery плагин для получения данных формы + ajax аплоадер файлов

Reading time 30 min
Views 19K
imageДоброго времени суток, уважаемое Хабросообщество! Не пытаясь скрыть легкое волнение, публикую свой первый пост, темой которого стал мой скромный jQuery-велосипед для быстрого и безболезненного сбора данных, введенных пользователем на странице (сразу небольшая оговорка для тех, кто закричит — «ату его! Зачем еще один подобный плагин?», отвечу: во-первых — а быть может именно это кто-то из хаброчитателей и искал, во-вторых — для саморазвития, в-третьх: для собственного удобства и безболезненного использования в своих програмных продуктах).

Итак, помимо, собственно моего небольшого плагина, нам понадобится сам jQuery последней версии, а так же небольшой плагин к нему, реализующий преобразование объектов\массивов в json-строку, для того, чтобы полученные данные можно было быстренько передавать на сервер.
updated

updated 2 — добавлена мультизагрузка!



А теперь приступим к описанию методов плагина. Их немного :) Точнее — три.

Первый: $.form.get(formName,toJson,multi). Функция возвращает данные с указанной формы в виде массива или json-строки. Коротко о главном, то есть о входных переменных.
  • formName — как следует из названия, передаем сюда имя формы.
  • toJsontrue/false. Если true — то возвращается json-строка, содержащая значения формы. Если же false — то объект. По умолчанию — true.
  • multi — так же принимает true/false. Если передано true, то функция понимает, что инпутов с одинаковым именем на странице может быть несколько, и собирает значения всех этих одноименных элементов формы в понумерованный массив. Передавая false, мы указываем, что одноименных input'ов на форме не предвидится, и можно не колеблясь запихивать значения в виде строки (исключения составляют элементы select с аттрибутом multiple и чекбоксы). По-умолчанию передается false;

Второй: $.form.set(formName,elemName,value) Служит для установки новых значений для элемента формы. Входные параметры:
  • formName — то же, что и в предыдущей функции.
  • elemName — значение аттрибута name элемента формы
  • value — строка или массив, содержащий новые значения элемента.

Третий: $.form.save(params) По сути дела, это обертка, получающая необходимую форму и отправляющая ее посредством post запроса на сервер. Входной параметр один, это объект следующего вида:
{
    name: "", //имя формы
    url: "", //адрес серверного скрипта
    params: {}, //объект, содержащий дополнительные параметры, передаваемые серверному скрипту
    multi: false, //то же, что и в предыдущих функциях
    test: false, //true/false — если true, то ответ сервера выводится с помощью функции alert
    callback: function(data){}, //функция, вызываемая после выполнения post-запроса. Принимает в качестве единственного параметра ответ сервера.
}
Если какой-то из параметров не передан, то они заменятся на значения по-умолчанию. Но понятно, что если не передать name и url, то получится билеберда =)

Ну и последнее: для того, чтобы указать имя формы для элементов input, select и textarea я использовал аттрибут class. Изначально попробовал ввести свой аттрибут form, но это оказалось не кроссбраузерно)


Код плагина в конце статьи

UPD

Итак, обновил скрипт и демо. Теперь плагин поддерживает ajax-upload файлов с прогрессбаром. Flash для загрузчика не требуется. Для отображения прогрессбара используется jQueryUI.

Для того, чтобы использовать input:file с нашим плагином, добавляем на страницу элемент, подобный этому:
<input type=«file» name=«file» class=«upload»>
Далее, на событие document.ready вешаем функцию, преобразующую этот инпут в наш ajax-загрузчик:

$.form.makeUpload('.upload[name=test]',{
    upload:'upload.php?action=uploadFile',
    progress:'upload.php?action=progress',
    directory:'uploads',
    multi:true,
    autoUpload:true
});

Вот что получается в итоге:
image

Получать данные с таким элементов можно все той же функцией $.form.get

Возвращаемые после загрузки файла значения:
  • directory — директория на сервере, куда был загружен файл
  • serverFileName — имя файла на сервере
  • path — directory+"/"+serverFileName
  • size — размер файла в байтах
  • isUploaded — 0 или 1. А был ли вообще загружен файл? :)

Функция $.form.makeUpload принимает два входящих параметра
  • multi — разрешить мультизагрузку, true\false. False по-умолчанию
  • selector — селектор объекта\объектов, которые нужно преобразовать в загрузчик
  • params — объект со следующими ключами:
    • upload — адрес серверного скрипта, который отвечает за upload файла
    • progress — адрес серверного скрипта, который отображает прогресс загрузки файла
    • serverFileName — имя, которое будет присвоено файлу на сервере. Если в этом параметре встречается слово %real%, то оно заменяется на настоящее имя файла
    • directory — директория, в которую будет загружен файл
    • autoUpload — true\false — начинать ли аплоад сразу после выбора файла
    • onSelect — функция, в которую передается объект, который в данный момент я и описываю) Вызывается в тот момент, когда был выбран файл
    • onStart — функция, которая вызывается, когда стартавал аплоад файла
    • onComplete — также функция. Вызывается в тот момент, когда загрузка файла завершена. Принимает в качестве параметра объект, содержащий ключи path (путь до файла на сервере), size (размер в байтах), name (имя файла)
PHP код файла upload.php, который и аплоадит и показывает прогресс аплоада:

  1. <?php
  2. function uploadFile()
  3. {
  4.   if (sizeof($_FILES))
  5.   {
  6.     $dir = getDir();
  7.     if ($dir !='' && !is_dir($dir))
  8.       mkdir($dir);
  9.     foreach($_FILES as $file)
  10.     {
  11.       $filename = str_replace('%real%', $file['name'], $_POST[$_POST['inputName'].'_serverFileName_def']);
  12.       move_uploaded_file($file['tmp_name'], $dir."/".$filename);
  13.     }
  14.   }
  15. }
  16.  
  17. function getDir()
  18. {
  19.   $dir = '';
  20.   if ($_POST[$_POST['inputName'].'_directory'] !='') $dir = $_POST[$_POST['inputName'].'_directory'];
  21.   return $dir;
  22. }
  23.  
  24. function progress()
  25. {
  26.   if (!isset($_POST['file'])) die();
  27.   $filename = $_POST['file'];
  28.   if (function_exists("uploadprogress_get_info"))
  29.   {
  30.     $r = uploadprogress_get_info($_POST['key']);
  31.     if (is_array($r))
  32.       $r = array_merge($r,array("result"=>1));
  33.     else
  34.     {
  35.       $size = 0;
  36.       if (file_exists($_POST['file'])) $size = filesize($_POST['file']);
  37.       $r = array("result" => -1, "size"=> $size);
  38.     }
  39.     echo json_encode($r);
  40.   }
  41.   else
  42.   {
  43.     $size = 0;
  44.     if (file_exists($_POST['file'])) $size = filesize($_POST['file']);
  45.     echo json_encode(array("result" => 0,"size" => $size));
  46.   }
  47. }
  48. if (isset($_GET['action']))
  49. {
  50.   if($_GET['action'] == 'uploadFile') uploadFile();
  51.   elseif ($_GET['action'] == 'progress') progress();
  52. }
  53. ?>
* This source code was highlighted with Source Code Highlighter.


Ну. пока вроде все. Спасибо всем за внимание )



Спасибо всем за карму! Перес в jquery.

Внимание тем кто уже загружал плагин! jquery.form.js и upload.php изменены. Добавлена мультизагрузка и исправлен один баг!

Плагин уже используется в личном кабинете компании СМС услуги, работает там ишачком на нужды клиентов, заливающих excel-файлы с базами абонентов для последующих смс рассылок. =) В числе клиентов куча офисных работников с IE самых разных видов, ни у кого проблем при аплоаде не возникает. Пользуйтесь ;)

Так как хостинг, держащий эти файлы пропал, выкладываю код плагина здесь, демка пропала без вести ) Не забудьте подключить jQuery и jQuery UI!

jQuery.form = {<br>    set:function(form,name,values)<br>    {<br>        var selector = "."+form+"[name="+name+"]";<br>        if ($(selector).is(':checkbox'))<br>        {<br>            if (!$.isArray(values)) values = new Array(values);<br>            $(selector).removeAttr("checked");<br>            for (var i = 0; i < values.length; i++)<br>                $(selector+"[value='"+values[i]+"']").attr("checked","checked");<br>            return;<br>        }<br>        if ($(selector).is(':radio'))<br>        {<br>            $(selector).removeAttr("checked");<br>            $(selector+"[value='"+values+"']").attr("checked","checked");return;<br>        }<br>        $(selector).val(values);<br>        return;<br>    },<br>    save:function(params)//name,url,key,callback,params,multi<br>    {<br>         var p = {<br>            name:'form',<br>            url:'',<br>            key:null,<br>            test:false,<br>            callback:function(){},<br>            params:{},<br>            multi:false<br>        }<br>        p = $.extend(p,params);<br>        var form = $.form.get(p.name,false,p.multi);<br>        if ( p.key !=null )<br>        {<br>            var c = {};<br>            c[p.key] = form;<br>            form = c;<br>        }<br>        form = $.toJSON(form);<br>        if (p.params == null) p.params = {p:form};<br>        else p.params.p = form;<br>        $.post(p.url,p.params,function(data){<br>            if (p.test)<br>                alert(data);<br>            data = $.parseJSON(data);<br>            if (data.code == 1)<br>                $.form.ok(p.name,data.descr,data.title);<br>            else $.form.error(p.name,data.descr,data.title);<br>            p.callback(data);<br>        },'html');<br>    },<br>    get:function(form,json,multi){<br>        if (multi == null) multi = false;<br>        if (json == null) json = true;<br>        if (form == null || form.length == 0)<br>        {<br>            if (!json) return {};<br>            else return $.toJSON({});<br>        }<br>        var selector = "input."+form+":radio:checked,input."+form+":checkbox:checked,input."+form+":text,input."+form+":hidden,input."+form+":file,input."+form+":password,textarea."+form+",select."+form;<br>        var inputs = $(selector);<br>        var values = {};<br>        $.each(inputs,function(){<br>            var name = $(this).attr("name");<br>            var value = $(this).val();<br>            <br>            if (($.isArray(value) && value[0] == null) || value == null)<br>                return;<br>            if ($(this).is(':file'))<br>            {<br>                var name = $(this).attr("name");<br><br>                var key = $(this).attr("key");<br>                var s = "input[type=hidden][key="+key+"]";            <br>                var v = {};<br>                v.directory = $(s+"[name="+name+"_directory]").attr("value");<br>                v.serverFileName = $(s+"[name="+name+"_serverFileName]").attr("value");<br>                v.path = $(s+"[name="+name+"_path]").attr("value");<br>                v.size = $(s+"[name="+name+"_size]").attr("value");<br>                v.isUploaded = $(s+"[name="+name+"_isUploaded]").attr("value");<br>                if (v.isUploaded == false || v.isUploaded == 0 || v.isUploaded == "0") return;<br>                if (!multi && !$.form.intervals[key].wasMulti) values[name] = v;<br>                else<br>                {<br>                    if (values[name] == null) values[name] = new Array();<br>                    values[name][values[name].length] = v;<br>                }<br>                return;<br>            }<br>            if (multi || $(this).is(':checkbox'))<br>            {<br>                if ($(this).is(":checkbox") && !$(this).attr("checked")) return;<br>                if (values[name] == null) values[name] = new Array();<br>                values[name][values[name].length] = value;<br>            }<br>            else<br>            {<br>                values[name] = value;<br>            }<br>        });<br><br>        if (json == false)<br>            return values;<br>        else<br>            return $.toJSON(values);<br>    },<br>    addInputFile:function(form,name,container)<br>    {<br>        var key = $('.'+form+'[name='+name+']').attr("key");<br><br>        var p = {};<br>        var oldparams = $.form.intervals[key];<br>        p.upload = oldparams.upload;<br>        p.progress = oldparams.progress;<br>        p.serverFileName = oldparams.serverFileName_def;<br>        p.directory = oldparams.directory;<br>        p.autoUpload = oldparams.autoUpload;<br>        p.multi = false;<br>        p.wasMulti = oldparams.wasMulti;<br>        p.onSelect = oldparams.onSelect;<br>        p.onStart = oldparams.onStart;<br>        p.onComplete = oldparams.onComplete;<br><br>        $(container).append("<input type=file name="+name+" class="+form+">");<br>        $.form.makeUpload('.'+form+'[name='+name+']',p,'multi');<br>    },<br>    uploadChange:function(key,param,value){<br>        $("input[key='"+key+"'][name='"+param+"']").attr("value",value);<br>    },<br>    makeUpload: function (selector,params,add){<br><br>        var elements = $(selector);<br>        $.each(elements,function(){<br>            var p = {<br>                upload:"/upload.php?action=uploadFile",<br>                progress:"/upload.php?action=progress",<br>                serverFileName:"%real%",<br>                directory:"",<br>                userParam:"",<br>                autoUpload:false,<br>                multi:false,<br>                wasMulti:false,<br>                onSelect:function(){},<br>                onStart:function(){},<br>                onComplete:function(){},<br>                name:$(selector).attr("name")<br>            }<br>            if ($(this).attr("key")!=null) return;<br>            var date = new Date();<br>            var key = date.getMilliseconds().toString()+date.getMinutes().toString()+date.getSeconds().toString()+Math.round(Math.random()*(1000000 - 0)).toString();<br>            p = $.extend(p,params);<br>            if (add == null)<br>                p.wasMulti = p.multi;<br>            else (p.wasMulti = true);<br>            p.selector = "[type=file][key="+key+"]";<br>            p.key = key;<br>            p.serverFileName_def = p.serverFileName;<br>            $(this).attr("key",key).change(function(){<br>                if ($("#submit_"+key).length == 0)<br>                {<br>                    $.form.intervals[key].onSelect($.form.intervals[key]);<br>                    $(this).after("<input type=submit value='Загрузить' id=submit_"+key+">");<br>                    if ($.form.intervals[key].autoUpload == true)<br>                        $("#submit_"+key).click();<br>                }<br>            });<br>            $(this).wrap("<form key='"+key+"' onsubmit=\"return $.form.uploadSelectedFile(this)\" name=wfUpload_"+key+" action='"+p.upload+"' target=iframe_"+key+" enctype='multipart/form-data' method=post></form>")<br>            $(this).parent().append("<input type=hidden name=key value='"+key+"'>");<br>            $(this).before('<input type="hidden" name="UPLOAD_IDENTIFIER" value="'+key+'">');<br>            var span = $(this).parent();<br>            var html =<br>                "<input type=hidden name='"+p.name+"_directory' key='"+key+"' value='"+p.directory+"'>" +<br>                "<input type=hidden name='"+p.name+"_serverFileName' key='"+key+"' value='"+p.serverFileName+"'>" +<br>                "<input type=hidden name='"+p.name+"_serverFileName_def' key='"+key+"' value='"+p.serverFileName_def+"'>" +<br>                "<input type=hidden name='"+p.name+"_path' key='"+key+"' value=''>" +<br>                "<input type=hidden name='inputName' key='"+key+"' value='"+p.name+"'>" +<br>                "<input type=hidden name='userParam' key='"+key+"' value='"+p.userParam+"'>" +<br>                "<input type=hidden name='"+p.name+"_isUploaded' key='"+key+"' value='0'>" +<br>                "<input type=hidden name='"+p.name+"_size' key='"+key+"' value=''>" +<br>                "<span uploaded=0 class=uploadDescr style='display:none' key='"+key+"'></span>" +<br>                "<iframe style='display:none;' onLoad=$.form.uploadComplete('"+key+"') name=iframe_"+key+" id=iframe_"+key+"></iframe>";<br>            $(span).append(html);<br>            if ($.form.intervals[key] == null)<br>                $.form.intervals[key] = p;<br>        });<br>        if (params.multi == true)<br>        {<br>            var l = elements.length;<br>            if (l > 0 && $('#more_files_'+$(elements[l-1]).attr("key")).length == 0)<br>                $(elements[l-1]).parent('form').after("<br><input type=submit value='Добавить еще один файл' onclick=$.form.addInputFile('"+$(elements[l-1]).attr("class")+"','"+$(elements[l-1]).attr("name")+"','#more_files_"+$(elements[l-1]).attr("key")+"')><br>").after('<span id=more_files_'+$(elements[l-1]).attr("key")+'></span>');<br>        }<br>    },<br>    uploadSelectedFile: function(s,params)<br>    {<br>        var p = {<br>            width:100,<br>            height:15<br>        }<br>        p = $.extend(p,params);<br>        var key = $(s).attr("key");<br>        $("#submit_"+key).attr("disabled","disabled");<br>        var span = $(s).children(".uploadDescr");<br>        var style = {display:'none', margin:"10px"};<br>        $(span).html("<div class=progressbar></div> <span style='text-align:right;' class=uploadedFileDetailInfo>Загрузка началась. Пожалуйста, подождите...</span>").css(style);<br>        var bar = $(span).children('div.progressbar');<br>        bar.progressbar({value: 0});<br>        //alert($.form.intervals[key].selector);<br>        var fname = $($.form.intervals[key].selector).attr("value");<br>        fname = fname.split('\\');<br>        $.form.intervals[key].realFileName = fname[fname.length-1];<br>        $.form.intervals[key].key = key;<br>        $.form.intervals[key].bar = bar;<br>        $.form.intervals[key].span = span;<br>        $($.form.intervals[key].span).attr("uploaded",0);<br>        $.form.intervals[key].onStart($.form.intervals[key]);<br>        //$.form.uploadProgress(key);<br>        $.form.intervals[key].interval = setInterval('$.form.uploadProgress("'+key+'")',1000);<br>    },<br>    uploadProgress: function(key){<br>        $.ajax({<br>            error:function(XMLHttpRequest, textStatus, errorThrown) {<br>            },<br>            start:function(){},<br>            beforeSend:function ( request ) {<br>                request.setRequestHeader( 'Cookie', document.cookie );<br>            },<br>            url:$.form.intervals[key].progress,<br>            data:{<br>                key:key,<br>                file:$.form.intervals[key].directory+"/"+$.form.intervals[key].realFileName<br>            },<br>            complete:function(){},<br>            dataType:'html',<br>            type:'post',<br>            success:function(data){<br>                data = $.parseJSON(data);<br>                if (data.result == 1)<br>                {<br>                    var timelast=data.time_last;<br>                    var total = data.bytes_total;<br>                    var speed = data.speed_average;<br>                    var bytes = data.bytes_uploaded;<br>                    var eta = data.est_sec;<br>                    var min = Math.round(eta / 60);<br>                    var sec = eta - min*60;<br>                    if(min==0){var time=sec+" сек."}else{var time=min+" мин."+sec+" сек."}<br>                    var speeds = $.form.speeds(speed);<br>                    var percents = Math.round(bytes * 100 / total);<br>                    $.form.intervals[key].size = total;<br>                    $($.form.intervals[key].span).children('.uploadedFileDetailInfo').html("<b>"+percents+"%</b>, <i>скорость:</i> <b>"+speeds+"</b>, <i>загружено</i> <b>"+$.form.fsize(bytes)+"</b> <i>из</i> <b>"+$.form.fsize(total)+"</b>");<br>                    $.form.intervals[key].bar.progressbar('value',percents);<br>                }<br>                if (data.result == -1 )<br>                    $.form.intervals[key].size = data.size;<br>                if (data.result == 0)<br>                {<br>                    $.form.intervals[key].size = data.size;<br>                    //alert(data.size);<br>                    if ($($.form.intervals[key].span).attr("uploaded") == 0)<br>                        $($.form.intervals[key].span).html("Сервер не поддерживает отображение процесса загрузки. Подождите завершения загрузки файла...");<br>                }<br><br>                $($.form.intervals[key].span).slideDown();<br>        }});<br>    },<br>    uploadCompleteAfterTimer:function(key){<br>        if ($.form.intervals[key].realFileName == null ) return;<br>        clearInterval($.form.intervals[key].interval);<br>        var size = "false";<br>        if ($.form.intervals[key].size != null) size = $.form.intervals[key].size;<br>        var serverFileName = $.form.str_replace('%real%',$.form.intervals[key].realFileName,$.form.intervals[key].serverFileName);<br>        var value = $.form.intervals[key].directory+"/"+serverFileName;<br>        $("input[key="+key+"][name="+$.form.intervals[key].name+"_path]").attr("value",value);<br>        $("input[key="+key+"][name="+$.form.intervals[key].name+"_isUploaded]").attr("value",1);<br>        $("input[key="+key+"][name="+$.form.intervals[key].name+"_size]").attr("value",size);<br>        $("input[key="+key+"][name="+$.form.intervals[key].name+"_serverFileName]").attr("value",serverFileName);<br>        $($.form.intervals[key].span).attr("uploaded",1);<br>        $("#submit_"+key).remove();<br>        $(".uploadDescr[key="+key+"]").html("Загрузка завершена!!!");<br>        var opt = {<br>            path:value,<br>            size:size,<br>            name:$.form.intervals[key].realFileName<br>        };<br>        $.form.intervals[key].onComplete(opt);<br>    },<br>    uploadComplete:function(key){<br>            setTimeout("$.form.uploadCompleteAfterTimer('"+key+"')",500);<br>    },<br>    intervals:{},<br>    "fsize":function(x) {<br>        x = Math.round(x / 1024);<br>        if (x < 1000) {<br>            return x + " " + "Кб";<br>        }<br>        x = Math.round(x * 100 / 1024) / 100;<br>        return x + " " + "Мб";<br>    },<br>    "speeds":function (x) {<br>        x = Math.round(x / 1024);<br>        if (x < 1000) {<br>            return x + " " + "Кб/сек";<br>        }<br>        x = Math.round(x * 100 / 1024) / 100;<br>        return x + " " + "Мб/сек";<br>    },<br>    "str_replace":function (search, replace, subject, count) {<br>        var i = 0, j = 0, temp = '', repl = '', sl = 0, fl = 0,<br>                f = [].concat(search),<br>                r = [].concat(replace),<br>                s = subject,<br>                ra = r instanceof Array, sa = s instanceof Array;<br>        s = [].concat(s);<br>        if (count) {<br>            this.window[count] = 0;<br>        }<br><br>        for (i=0, sl=s.length; i < sl; i++) {<br>            if (s[i] === '') {<br>                continue;<br>            }<br>            for (j=0, fl=f.length; j < fl; j++) {<br>                temp = s[i]+'';<br>                repl = ra ? (r[j] !== undefined ? r[j] : '') : r[0];<br>                s[i] = (temp).split(f[j]).join(repl);<br>                if (count && s[i] !== temp) {<br>                    this.window[count] += (temp.length-s[i].length)/f[j].length;}<br>            }<br>        }<br>        return sa ? s : s[0];<br>    }<br>}<br><br>* This source code was highlighted with Source Code Highlighter.
Tags:
Hubs:
+61
Comments 98
Comments Comments 98

Articles