js加php实现同时并发上传多个文件或文件夹

js+php实现同时并发上传多个文件或文件夹

js加php实现同时并发上传多个文件或文件夹

当我们上传大型文件或者很多文件(比如一个文件夹,包含很多子文件和子目录),如果采用传统的方式,一个接一个上传,可定很慢,那么有没有一种方式可以同时将所有的文件并行上传到服务器,最后汇总一下,有点像hadoop的map/reduce的模式,答案肯定是有的,js本身就是机遇event的异步单线程设计,异步的特性决定了不必等地io时间,直接去处理其他的事情。好了,那么首先解决怎么同时上传多个文件和文件夹的事情。

一、怎么选择文件夹和多个文件?

上传多个文件(multiple 属性ie9以上才支持)

<input type="file" name="file" multiple="multiple" />

上传整个文件夹目录(webkitdirectory属性只有chrome及chrome内核的浏览器和火狐支持)

  <input type='file'  name="file" webkitdirectory />//

那么接下来解决怎么同时上传的问题

二、怎么同时上传?

思路一、我们将多个文件放进Formdata构建form数据结合ajax来启动上传,代码如下

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>BFW NEW PAGE</title>
    <script id="bfwone" data="dep=jquery.17&err=0" type="text/javascript" src="http://repo.bfw.wiki/bfwrepo/js/bfwone.js"></script>
    <script type="text/javascript">


        var files = [];

        bready(function() {
            $("#uploadfolder").change(function() {

                files = this.files;


            });

            $("#upload-btn").click(function() {

                var fd = new FormData();

                console.log(files);

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

                }


                $.ajax({
                    type: 'POST',
                    url: "/FolderUpload",
                    data: fd,
                    cache: false,
                    processData: false,
                    contentType: false,
                    dataType: 'json',

                    success: function (ret) {},
                    complete: function(XMLHttpRequest, textStatus) {}
                });

            });
        });
    </script>
    <style>
    </style>
</head>
<body>


    <input type='file' id="uploadfolder" name="file" multiple="multiple" />

    <button id="upload-btn">上传多个文件</button>


</body>
</html>

那么后端php怎么接受呢

<?php

function reArrayFiles(&$file_post) {

    $file_ary = array();
    $file_count = count($file_post['name']);
    $file_keys = array_keys($file_post);

    for ($i=0; $i<$file_count; $i++) {
        foreach ($file_keys as $key) {
            $file_ary[$i][$key] = $file_post[$key][$i];
        }
    }

    return $file_ary;
}


if ($_FILES['file']) {
    $file_ary = reArrayFiles($_FILES['file']);

    foreach ($file_ary as $file) {
        move_uploaded_file($file["tmp_name"],"upload/" . $file["name"]);//移动上传文件到自定义目录存储
        print 'File Name: ' . $file['name'];
        print 'File Type: ' . $file['type'];
        print 'File Size: ' . $file['size'];
    }
}

?>

好了,多文件上传解决了,但是有个问题,如果文件比较多,比较大,就会出现卡住,超时等情形,那怎么办呢,

思路二、我们可以将每个文件上传单独一个ajax上传到后端,后端启用多线程或多进程来同时处理上传,这样就加快了上传的速度。

今天我们通过promise异步调用来执行并行上传。

Promise,他是一个对象,是用来处理异步操作的,可以让我们写异步调用的时候写起来更加优雅,更加美观便于阅读。

具体代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>BFW NEW PAGE</title>
    <script id="bfwone" data="dep=jquery.17&err=0" type="text/javascript" src="http://repo.bfw.wiki/bfwrepo/js/bfwone.js"></script>
    <script type="text/javascript">


        var files = [];

        bready(function() {
            $("#uploadfolder").change(function() {

                files = this.files;


            });

            $("#upload-btn").click(function() {
                var fileret = new Array(); //上传结果存储数组
                const uploadfilepromise = function(file) {
                    return new Promise((resovle, reject) => {
                        var fd = new FormData();
                        fd.set('file', file);
                        $.ajax({
                            type: 'POST',
                            url: "/FolderUpload",
                            data: fd,
                            cache: false,
                            processData: false,
                            contentType: false,
                            dataType: 'json',
                            error: function (ret) {

                                reject(ret);
                            },
                            success: function (ret) {
                                if (ret.err) {
                                    fileret.push(ret); //如果上传失败
                                }
                                resovle(ret);
                            },
                            complete: function(XMLHttpRequest, textStatus) {}
                        });

                    });
                };


                var filellist = new Array(); //本地文件
                for (i = 0; i < files.length; i++) {
                    filellist.push(files[i]);

                }


                console.log(filellist);
                let promises = filellist.map((item, index) => {
                    return uploadfilepromise(item);
                });

                // 并行执行
                Promise.all(promises)
                .then(() => {

                    if (fileret.length > 0) {
                        console.log('部分上传失败')
                    } else {
                        console.log('全部上传成功')
                    }

                    fileret = null;


                    console.log('上传完成')
                })
                .catch(() => {
                    console.log('上传错误error')
                })


            });
        });
    </script>
    <style>
    </style>
</head>
<body>


    <input type='file' id="uploadfolder" name="file" multiple="multiple" />

    <button id="upload-btn">上传多个文件</button>


</body>
</html>

那么后端php还是采用简单的单一文件上传方式,为啥,因为后端是多线程多进程的,同时处理,每个进程或线程处理一个文件。

<?php
if ($_FILES["file"]["error"] > 0)
  {
  echo "Error: " . $_FILES["file"]["error"] . "<br />";
  }
else
  {
  move_uploaded_file( $_FILES["file"]["tmp_name"],"upload/" .  $_FILES["file"]["name"]);//移动上传文件到自定义目录存储
  echo "Upload: " . $_FILES["file"]["name"] . "<br />";
  echo "Type: " . $_FILES["file"]["type"] . "<br />";
  echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
  echo "Stored in: " . $_FILES["file"]["tmp_name"];
  }
?>

文件夹的上传方式与这个雷同,请查看另一篇博文,http://blog.bfw.wiki/user10/15590389971771170059.html

{{collectdata}}

网友评论1

  1. # 90
    不错
    lick 2022-07-22回复