CodeIgniter AJAX文件上传,上传时$_FILE为空

Posted

技术标签:

【中文标题】CodeIgniter AJAX文件上传,上传时$_FILE为空【英文标题】:CodeIgniter AJAX file upload, $_FILE is empty when upload 【发布时间】:2016-04-16 09:29:14 【问题描述】:

我正在编写一个脚本,该脚本使用 jQuery 通过拖放上传文件,该脚本在拖放时读取文件并将其添加到文件数组中,如下所示:

var files = [];

$(document).ready(function() 
    jQuery.fn.dropbox = function(config) 
        var dragging = 0;
        var dragEnter = function(e) 
            e.stopPropagation();
            e.preventDefault();
            dragging++;
            $(".external-drop-indicator").fadeIn();
            $(".toast.toast-success").fadeIn().css("display", "inline-block");;
            return false;
        ;
        var dragOver = function(e) 
            e.stopPropagation();
            e.preventDefault();
            return false;
        ;
        var dragLeave = function(e) 
            e.stopPropagation();
            e.preventDefault();
            dragging--;
            if(dragging === 0) 
                $(".external-drop-indicator").fadeOut();
                $(".toast.toast-success").fadeOut();
            
            return false;
        ;
        var drop = function(e) 
            var dt = e.dataTransfer;
            var files_upload = dt.files;

            e.stopPropagation();
            e.preventDefault();

            if(files_upload && files_upload.length > 0 && config.onDrop !== undefined) 
                files.push(files_upload);
                config.onDrop(files_upload);
            

            $(".external-drop-indicator").fadeOut();
            $(".toast.toast-success").fadeOut();

        ;
        var applyDropbox = function(dropbox) 
            dropbox.addEventListener('dragenter', dragEnter, false);
            dropbox.addEventListener('dragover', dragOver, false);
            dropbox.addEventListener('dragleave', dragLeave, false);
            dropbox.addEventListener('drop', drop, false);
        ;
        return this.each(function() 
            applyDropbox(this);
        );
    ;
);

总而言之,它的作用是添加一个扩展的 jQuery 函数,以便在应用该函数的网站的某个元素上启用拖放。

然后我将扩展功能应用到正文,以启用文件拖放功能,如下所示:

$(document).ready(function() 
    $('body').dropbox(
        onDrop: function(f) 
            $(f).each(function(idx, data) 
                var file_name = data.name;
                var extension = file_name.split('.');
                file_name = extension[0];
                extension = extension[1];
                if(extension == 'pdf' || extension == 'xls') 
                    showAjaxModal(base_url + 'index.php?modal/popup/file_create/' + folder_id + '/' + extension + '/' + file_name);
                 else 
                    $(".upload-area").append('<div class="alert alert-danger" style="display:inline-block;width:480px;"><strong>Error!</strong> File type is incorrect.</div>');
                
            );
        
    );
);

总而言之,它的作用是将拖放功能添加到正文中,当文件被删除时,它会通过拆分名称和扩展名来检测文件的扩展名,以便我可以验证文件的扩展名被丢弃是正确的。然后它会继续显示一个模式来填充有关正在上传的文件的信息以供以后提交,如果文件扩展名是正确的。

然后当模态弹出时,我继续使用 CodeIgniter 的函数“form_open()”填充文件信息:

<?php echo form_open(base_url() . 'index.php?client/file/create/' . $param2, array('class' => 'form-horizontal form-groups-bordered validate ajax-upload', 'enctype' => 'multipart/form-data')); ?>

    <div class="col-md-4 file-info">
        <div class="icon-<?=($param3 == 'pdf' ? 'pdf' : 'document')?>"></div>
        <p>
            <?php echo $param4;?>
        </p>
    </div>

    <div class="col-md-8 new-file">

        <div class="form-group">
            <div class="col-sm-12">
                <div class="input-group">
                    <input type="text" class="form-control" name="tags" data-validate="required" data-message-required="Field is required" placeholder="File tags" value="" autofocus>
                </div>
            </div>
        </div>

        <div class="form-group">
            <div class="col-sm-12">
                <div class="input-group">
                    <input type="text" class="form-control" name="name" data-validate="required" data-message-required="Field is required" placeholder="File name" value="" autofocus>
                </div>
            </div>
        </div>

        <div class="form-group">
            <div class="col-sm-12">
                <div class="input-group">
                    <textarea rows="5" class="form-control" name="description" data-validate="required" data-message-required="Field is required" placeholder="File description" value="" autofocus></textarea>
                </div>
            </div>
        </div>

    </div>

    <div class="form-group">
        <div class="col-sm-offset-4 col-sm-7">
            <button type="submit" class="btn btn-info" id="submit-button">Upload</button>
            <span id="preloader-form"></span>
        </div>
    </div>

<?php echo form_close(); ?>

这基本上创建了一个稍后将通过 Ajax 提交的表单。

现在我继续通过 jQuery 处理文件提交,以便将信息与正在上传的文件一起发送,如下所示:

$(document).ready(function(e) 
    $('.ajax-upload').submit(function(e) 
        e.preventDefault();

        var $elm = $(this);

        var fd = new FormData();

        for(var i = 0; i < files.length; i++) 
            fd.append("file_" + i, files[i]);
        

        var form_data = $(".ajax-upload").serializeArray();

        $.each(form_data, function(key, input) 
            fd.append(input.name, input.value);
        );

        var opts = 
            url: $elm.attr('action'),
            data: fd,
            cache: false,
            contentType: false,
            processData: false,
            type: 'POST',
            beforeSend: uValidate,
            success: showResponse
        ;

        if(fd.fake) 
            opts.xhr = function() 
                var xhr = jQuery.ajaxSettings.xhr();
                xhr.send = xhr.sendAsBinary;
                return xhr;
            
            opts.contentType = "multipart/form-data; boundary=" + fd.boundary;
            opts.data = fd.toString();
        

        jQuery.ajax(opts);

        return false;
    );
);

基本上,表单默认操作被覆盖,并且在之前的代码块中提交的用于拖放功能的文件现在被附加到 formData,该表单数据随后被我提交的表单上的表单数据连接。然后通过 AJAX 调用发送 formData。

现在控制器看起来像这样,它处理 AJAX 调用,然后在模型上执行 File Upload 方法,如下所示:

function file($param1 = '', $param2 = '', $param3 = 0) 
    if ($this->session->userdata('client_login') != 1) 
        $this->session->set_userdata('last_page', current_url());
        redirect(base_url(), 'refresh');
    

    if ($param1 == 'create')
        $this->crud_model->create_file($param2);

    if ($param1 == 'edit')
        $this->crud_model->update_file($param2);

    if ($param1 == 'delete')
        $this->crud_model->delete_file($param2, $param3);

    $page_data['page_name'] = 'files';
    $page_data['page_title'] = 'Files List';
    $page_data['folder_id'] = $param3;
    $this->load->view('backend/index', $page_data);

这里是模型方法:

function create_file($folder_id) 
    $data['name']         =   $this->input->post('name');
    $data['tags']         =   $this->input->post('tags');
    $data['description']  =   $this->input->post('description');
    $data['type']         =   'file';
    $data['folder_id']    =   $folder_id;
    $data['client_id']    =   $this->session->userdata('login_user_id');

    $config['upload_path'] = 'uploads/tmp/';
    $config['allowed_types'] = '*';
    $config['max_size'] = '100';

    $this->load->library('upload');
    $this->upload->initialize($config);

    var_dump($_FILES);
    die();

    foreach($_FILES as $field => $file)
    
        //var_dump($_FILES); die();                
        // No problems with the file
        if($file['error'] == 0)
        
            // So lets upload
            if ($this->upload->do_upload($field))
            
                $data = $this->upload->data();
                echo $data['full_path'];
            
            else
            
                $errors = $this->upload->display_errors();
                var_dump($errors);
            
        
    

    $this->db->insert('client_files' , $data);

所以基本上发生的情况是 $_FILES 数组为空,文件没有上传。

在 Chrome 的开发者工具中查看的“请求负载”如下所示:

------WebKitFormBoundaryvVAxgIQd6qU8BtkF
Content-Disposition: form-data; name="file_0"

[object FileList]
------WebKitFormBoundaryvVAxgIQd6qU8BtkF
Content-Disposition: form-data; name="tags"

bsd
------WebKitFormBoundaryvVAxgIQd6qU8BtkF
Content-Disposition: form-data; name="name"

asd
------WebKitFormBoundaryvVAxgIQd6qU8BtkF
Content-Disposition: form-data; name="description"

asd

我从模型上的 var_dump() 得到的响应如下:

array(0) 

我已经尝试过这个问题的解决方案:Sending multipart/formdata with jQuery.ajax 但到目前为止还没有运气。

知道我做错了什么以及如何解决这个问题吗?谢谢。

【问题讨论】:

您正在从 AJAX 发送 base_url + 'index.php?modal/popup/file_create/' + folder_id + '/' + extension + '/' + file_name,但服务器端只需要 function create_file($folder_id) 。查看您的路线和/或更改 CI 方法以接受正确数量的参数。 @Tpojka 该网址仅用于模式,实际的create_file($folder_id)... 在提交表单时被调用。它与实际的文件上传没有任何关系。 【参考方案1】:

这里的问题是我在 AJAX 调用中发送文件数组而不是单个文件,特别是在这部分代码中:

for(var i = 0; i < files.length; i++) 
    fd.append("file_" + i, files[i]);

解决方案是将文件逐个附加到 formData 而不是文件数组,如下所示:

for(var i = 0; i < files[0].length; i++) 
    fd.append("file_" + i, files[i]);

这将追加文件数组的每个文件,而不是文件数组本身,它解决了问题。

总之,我发送的是文件数组而不是单个文件,这方面的线索是请求显示的 [object FileList] 而不是文件信息,它现在发出这样的请求:

Content-Disposition: form-data; name="file"; filename="exfile.pdf"
Content-Type: application/pdf

【讨论】:

以上是关于CodeIgniter AJAX文件上传,上传时$_FILE为空的主要内容,如果未能解决你的问题,请参考以下文章

使用ajax在codeigniter中上传图片

Codeigniter 图片上传使用 jquery 和 ajax 错误 [重复]

如何在codeigniter中使用ajax上传图片?

CodeIgniter 上传进度

使用 codeigniter 上传画布图像

如何在 Codeigniter 中上传文件名时添加前缀?