如何上传完整文件夹及其文件

Posted

技术标签:

【中文标题】如何上传完整文件夹及其文件【英文标题】:How to Upload a Full Folder with their files 【发布时间】:2020-01-05 00:41:19 【问题描述】:

我得到了上传多个文件的代码,但现在我不知道如何上传包含多个文件的文件夹以及包含更多文件的子文件夹。等等 如您所见,我现在使用 javscript 获取文件,使用 php 处理和保存文件,文件大小约为 2MB 我试图通过 foreach 获得像 $_Folder 一样的东西,但它对我不起作用:/

Index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <title>Upload Files</title>

</head>

<body>
    <form method="post" enctype="multipart/form-data">
        <input type="file" name="files[]" multiple>
        <input type="submit" value="Upload File" name="submit">
    </form>

    <script src="upload.js"></script>
</body>

</html>

进程.php

<?php 
if ($_SERVER['REQUEST_METHOD'] === 'POST') 
    if (isset($_FILES['files'])) 
        $errors = [];
        $path = 'uploads/';
    $extensions = ['jpg', 'jpeg', 'png', 'gif'];

        $all_files = count($_FILES['files']['tmp_name']);
        for ($i = 0; $i < $all_files; $i++)   
        $file_name = $_FILES['files']['name'][$i];
        $file_tmp = $_FILES['files']['tmp_name'][$i];
        $file_type = $_FILES['files']['type'][$i];
        $file_size = $_FILES['files']['size'][$i];
        $file_ext = strtolower(end(explode('.', $_FILES['files']['name'][$i])));
        $file = $path . $file_name;
        if (!in_array($file_ext, $extensions)) 
            $errors[] = 'Extension not allowed: ' . $file_name . ' ' . $file_type;
        
        if ($file_size > 2097152) 
            $errors[] = 'File size exceeds limit: ' . $file_name . ' ' . $file_type;
        
        if (empty($errors)) 
            move_uploaded_file($file_tmp, $file);
        
    
    if ($errors) print_r($errors);
    

上传.js

const url = 'process.php';
const form = document.querySelector('form');

form.addEventListener('submit', e => 
    e.preventDefault();

    const files = document.querySelector('[type=file]').files;
    const formData = new FormData();

    for (let i = 0; i < files.length; i++) 
        let file = files[i];

        formData.append('files[]', file);
    

    fetch(url, 
        method: 'POST',
        body: formData
    ).then(response => 
        console.log(response);
    );
);

期待上传这样的东西

Upload:
-(1Folder)
--Image.png
--Imagen.jpg
--(2Folder)
---Image2.png
--(3Folder)
---Image3.jpg
--Imagen.gif

【问题讨论】:

出于安全原因,您无法通过 html 和 javascript 选择整个文件夹。您只能选择文件。 @Denhell 确实不能选择整个文件夹,但我认为这与浏览器和 HTTP 的工作方式有关,而不是与安全相关的任何事情。你在哪里读到这是一个安全限制?取决于浏览器的 UI 和上传控件,尽管您可能可以进入文件夹并按 Ctrl+A 一次选择所有文件 有什么替代方法吗? @MiguelAngelEsparzaCalero 取决于浏览器的 UI 和上传控件,尽管您可以进入文件夹并按 Ctrl+A 一次选择所有文件。或者另一个明显的替代方法是将您需要的所有文件放入一个 ZIP 文件中,然后上传。 【参考方案1】:

在现代 Chrome、Firefox 和 Edge 中,您可以设置一个 html 属性 webkitdiretory 以让文件输入变为目录选择窗口。如果您在选择文件夹后还使用multiple 属性,则所有内容(以及子文件夹中的内容)都将在.files 列表中

<input type="file" webkitdirectory multiple>

然后,您只需使用相同的代码来包含所有要上传的文件。

现在,如果您想保留文件夹结构,您还必须包含 webkitRelativePath,它保存您选择的文件夹中该文件的相对路径。并使用该路径在服务器上创建文件夹。

for (let i = 0; i < files.length; i++) 
    let file = files[i];
    let fileParamName = `file$i`;
    let filePathParamName = `filepath$i`;
    formData.append(fileParamName, file);
    formData.append(filePathParamName,file.webkitRelativePath);

然后在服务器上使用 filePathParamName 制作目录结构并将文件移动到它:

//Just for example
//make sure to used sanitized data in production
$folderpath = $path.dirname($_POST["filepath23"]);
$file = $path.$_POST["filepath23"];
$file_tmp = $_FILES["file23"]["tmp_name"];

//third option is for recursive folder creation (make subfolders)
mkdir($path,0644,true);
move_uploaded_file($file_tmp, $file)

为了更简单的方法,您可以将所有文件放入 javascript 中的 zip 文件中,然后上传单个 zip 文件并在服务器上解压缩。 Using JSZip 和 PHP ZipArchive class:

var zip = new JSZip();
for (let i = 0; i < files.length; i++) 
    let file = files[i];
    zip.file(file.webkitRelativePath, file);

zip.generateAsync(type:"blob")
   .then(function(content) 
     formData.append("folderzip",content);
     fetch(url, 
       method: 'POST',
       body: formData
     ).then(response => 
       console.log(response);
     ); 
   );

然后在php中解压文件夹到你想要的位置:

move_uploaded_file($file_tmp, $path);
$zip = new ZipArchive();
if($zip->open($path))
  $zip->extractTo($somepath);
  $zip->close();
  //delete zip file
  unlink($path);


使用 webkitRelativePath 列出文件的客户端演示:

var output = document.querySelector("#output");
document.querySelector("input").onchange = function() 
  var files = this.files;
  for (file of files) 
    output.insertAdjacentHTML('beforeend', `<div>$file.webkitRelativePath</div>`);
  
<input type="file" webkitdirectory multiple>

<div id="output"></div>

【讨论】:

您能否确认这是否是我在生产中可以信任的东西?这正是我正在寻找使用预签名 URL 将文件夹上传到 AWS s3 的内容。提前谢谢你。

以上是关于如何上传完整文件夹及其文件的主要内容,如果未能解决你的问题,请参考以下文章

Retrofit2文件上传下载及其进度显示

如何在codeigniter中获取上传文件的完整路径?

HTML表单完整文件夹上传[重复]

Google Forms文件上传完整示例

H5拖放+FormData接口+NodeJS,完整异步文件上传

在firefox中以html上传文件时获取文件完整路径[重复]