天天看點

原生input标簽實作ajax單檔案上傳和多檔案上傳

     自己還是一個菜鳥的時候,有次項目經理讓我用Java做一個多檔案上傳的功能。那時候技術學得很渣,最多隻能夠實作單檔案上傳。做了一個星期都沒有做出來,于是項目經理不留半點情面,當着辦公室所有人的面痛批我一頓,讓我顔面掃地,當時我對了他一句我大不了辭職嘛。那是我的悲傷記憶,最終和上級上司溝通半天後,确定留下來繼續幹。經曆一些事情後,一個人總是要成長的,我也一樣。如果那個簡單的功能都做不出來,那我還能夠幹什麼呢?除了提升自己我還能幹什麼呢?

      那次的問題最後是使用SWFUpload插件解決多檔案上傳問題的,多年之後的今天已經對檔案上傳信手拈來,不管是使用插件還是原生的input标簽。現在來分享一下使用原生input标簽上傳檔案的用法。

AJAX實作單檔案上傳

    <input type="file" name="file" id="uploadFile"  οnchange="uploadExcel()"  accept=".xls,.xlsx">

input标簽可以隐藏也可以顯示出來,樣式怎麼好看自己就怎麼改,type,name屬性不要忘記。主要是觸發input标簽的單擊事件,然後選擇檔案。accept屬性的作用是可以限制檔案的選擇類型,不在accept裡面的類型的檔案不會顯示出來。對一般人來說還是能起作用。選擇好檔案後,觸發下一個事件onchange,它是在檔案選擇好以後執行。重點來了,檔案選擇好以後需要做的事情,上代碼:

       let fileType = $('#uploadFile').val();

        fileType = fileType.substring(fileType.lastIndexOf('.'));

        //檔案類型校驗

        if(fileType !== '.xls' && fileType !== '.xlsx'){

            return msgError('操作提示:隻能上傳xls格式或者xlsx格式的檔案!');

        }

        //擷取單個檔案

        let file = document.getElementById(`uploadFile`).files[0];

       //必須建立一個FormData對象,然後将擷取到的檔案資料添加到對象中,并且要注意inputFile,解析時需要它。

        let formData = new FormData();

        formData.append('inputFile',file);

        $.ajax({

            url: '{{projcfg.appurl}}/api/cable/cable_manage_route/uploadExcel',

            type: 'post',

            data: formData,

            cache: false,//上傳檔案無需緩存

            contentType: false,//必須

            processData: false,//用于對data參數進行序列化處理 這裡必須false

            success: function (json) {

                if (json.success) {

                    msgSuccess(`消息提示:${json.msg}`);

                    $("#dataTable").datagrid("reload");

                }else {

                    msgError(`錯誤提示:${json.msg}`);

                }

                $('#importWin').dialog('close');

                $('#uploadFile').parent().html('<input type="file" name="file" id="uploadFile" οnchange="uploadExcel()"  accept=".xls,.xlsx">');

            },

            error: function () {

                msgError(`錯誤提示:導入Excel檔案出現異常,請稍後重試!`);

            },

        });

後端node解析核心代碼,使用formidable這個包進行解析,java則可以使用對應的包來進行解析。

 var form = new formidable.IncomingForm();   //建立上傳表單

            form.encoding = opts.encoding;    //設定字元集

            form.uploadDir = opts.uploadDir;     //設定上傳目錄

            form.keepExtensions = opts.keepExtensions;     //保留檔案字尾名

            form.maxFieldsSize = opts.maxFieldsSize;   //檔案大小

            form.parse(req, function (error, fields, files) {

                if (error) {

                    reject(error);

                } else {

                    if(files[opts.file_name]){//有附件上傳 和uploadFile的名字一樣

                        let filePath = files[opts.file_name].path;

                        let fileType = filePath.substr(filePath.lastIndexOf(".") + 1);

                        if (opts.fileTypes.indexOf(fileType) == -1) {

                            delFile(filePath);

                            reject(`Excel檔案格式錯誤,請上傳格式為${opts.fileTypes}類型的檔案!`);

                        } else {

                            //處理Excel檔案

                            let userId = 8;//req.session.current_user._id;

                            resole(filePath);

                        }

                    }else{//沒有附件上傳

                        reject('沒有附件上傳!');

                    }

                }

            });

測試後完全可行,項目已經上線正常運作。

AJAX實作多檔案上傳

       多檔案上傳和單檔案上傳類似,隻需要做少量的修改即可。

<input type="file" name="upload_file" id="upload_file" οnchange="upload_picture()" accept=".jpg, .png" multiple/>

第一個需要修改的地方是添加一個multiple屬性,它的作用是你可以同時選擇多個檔案。

function upload_picture() {

        //擷取檔案資料

        let formData = new FormData();

        let files = document.getElementById("upload_file").files;

        formData.enctype = "multipart/form-data";

        let fileArray = [].slice.call(files,0);//類數組轉換為數組

        let fileType = '', file = '', fileSize = '';

        //第二個需要修改的地方,循環驗證選擇的檔案和添加檔案資料到FormData中

        for(let k = 0,klen = fileArray.length; k < klen; k++){

            file = fileArray[k];

            fileType = fileArray[k].name.substring(fileArray[k].name.lastIndexOf('.') + 1);

            if(fileType !== 'jpg' && fileType !== 'png'){

                return layer.msg(`操作提示:隻能上傳jpg格式或者png格式的圖檔!`);

            }

            let fileSize = Math.floor(file.size/(1024*1024));

            if(fileSize > 2){

                return layer.msg(`操作提示:隻能上傳大小不超過2MB的圖檔!`);

            }

            formData.append(`upload_file${k}`,file);//循環周遊把檔案對象插到formData對象上

        }

        if(fileArray.length === 0){

            return layer.msg(`操作提示:請先選擇需要上傳的圖檔!`);

        }

        //送出資料

        $.ajax({

            url: '{{projcfg.base}}/api/kfz/model_manage_route/upload_picture.do',

            type: 'post',

            data: formData,

            cache: false,//上傳檔案無需緩存

            contentType: false,//必須

            processData: false,//用于對data參數進行序列化處理 這裡必須false

            success: function (result) {

                if (result.success && result.code === 2000) {

                    layer.msg(`消息提示:${result.msg}`);

                    file_arr = result.data;

                }else {

                    layer.msg(`錯誤提示:${result.msg}`);

                }

                //重新建立檔案上傳标簽

                $('#upload_file_div').html(`<input type="file" name="upload_file" id="upload_file"  οnchange="upload_picture()" accept=".jpg, .png" multiple/>`);

            },

            error: function () {

                layer.msg(`錯誤提示:上傳圖檔出現異常,請稍後重試!`);

            },

            timeout: 180000,

            complete: function () {

                //$.messager.progress('close');

            }

        });

    }

後端node解析核心代碼:

 var form = new formidable.IncomingForm();   //建立上傳表單

            form.encoding = opts.encoding;    //設定字元集

            form.uploadDir = opts.uploadDir;     //設定上傳目錄

            form.keepExtensions = opts.keepExtensions;     //保留檔案字尾名

            form.maxFieldsSize = opts.maxFieldsSize;   //檔案大小

            form.parse(req, function (error, fields, files) {

                if (error) {

                    reject(error);

                }else {

                    //檔案處理

                    let temp_file,file_path,file_type,flag,path_arr = [],obj = {};

                    let type_arr = opts.fileTypes.split('|');

                    Object.keys(files).forEach(function (file_key) {//第三個需要修改的地方,file_key即為上傳時添加的不同的名稱

                        temp_file = files[file_key];

                        obj = {};

                        if(temp_file){//有附件上傳

                            let file_path = temp_file.path;

                            let file_type = file_path.substr(file_path.lastIndexOf("."));

                            flag = flag;

                            type_arr.forEach(function (item) {

                                if(item === file_type){

                                    flag = true;

                                }

                            });

                            if(!flag){

                                delFile(file_path);//檔案類型不比對

                                reject(`圖檔格式錯誤,請上傳格式為${opts.fileTypes}類型的圖檔!`);

                            }else {

                                //将圖檔移動到指定的檔案夾

                                let temp_path = path.join(opts.uploadDir, opts.nowDate, 'verification');

                                let desc_path = path.join(temp_path, Math.random().toString().substring(2,12) + file_type);

                                //處理路徑

                                obj.file_path = desc_path.replace(/\\/g,'/');

                                obj.file_name = temp_file.name;

                                path_arr.push(obj);

                                mkdirsSync(temp_path,'0779');//建立檔案夾

                                fs.renameSync(file_path,desc_path);

                            }

                        }

                    });

                    if(path_arr.length === Object.keys(files).length){

                        //傳回檔案路徑

                        resole(path_arr);

                    }else{

                        reject('檔案上傳出現錯誤,請稍後重試!');

                    }

                }

            });

項目中已經正式采用這種方法,完全可行,目前還沒有發現其他問題。

注意事項

       如果有檔案選擇錯誤的話或者是想重新選擇,在不重新整理頁面的情況下,則需要替換掉原來的input标簽。這個好比一次性注射器用一次就扔掉,當然吸毒的除外,每一個用于檔案上傳的input标簽隻能使用一次。采用這種處理方式可以将檔案上傳和資料送出完全分離開來,處理起來非常友善。如果使用form表單送出的方式,則檔案上傳和其他表單資料一起送出,不排除會容易出現一些未知錯誤。

繼續閱讀