如何使用PHP连接从Dropzone.js上传的分块文件? [英] How to concatenate chunked file uploads from Dropzone.js with PHP?

查看:126
本文介绍了如何使用PHP连接从Dropzone.js上传的分块文件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Dropzone.js拍摄各种类型的文件(包括图像和非图像,例如PDF),并将它们以1mb的大小上传到我们的服务器。然后,我尝试使用PHP连接文件,然后将它们上传到我们公司的FileMaker数据库中。

I'm using Dropzone.js to take files of various types (including images and non-images, like a PDF), and upload them in 1mb chunks to our server. I'm then attempting to concatenate the files with PHP and later upload them to our company's FileMaker database.

到目前为止,我已经能够将文件上传到块,因为他们应该。我将它们全部存储在一个具有相同代号的临时上载文件夹中,并在每个名称的末尾附加 -INDEX#(INDEX#是要上传的数据块#,由Dropzone传递)。

So far I've been able to get the files to upload in the chunks, as they should. I store them all in a temporary "uploads" folder with the same "codename," with "-INDEX#" appended to the end of each name (INDEX# being the chunk # being uploaded, as passed by Dropzone).

我已经将失败的PHP部分隔离到已经上传的块中,以获取内容。具体来说,当我去获取内容时,我尝试通过使用PHP的 realpath来获取数据块的绝对路径,以获取绝对路径,并同时对文件的存在进行真假检查,以将块的文件路径保存到变量中。毫无疑问,PHP无法看到文件。

I've isolated the failure to the portion of the PHP that loops through the already uploaded chunks to grab the content. Specifically, when I go to fetch the content, I attempt to save the file path of the chunk to a variable by using PHP's "realpath" to both get the absolute path, and also serve as a true/false check of the file's existence. Without fail, PHP is unable to "see" the file.

我不是文件夹权限方面的专家,因此很有可能与之相关,并且我只是不知道该如何处理。您会看到我确实在uploads /目录的PHP顶部尝试了chmod。

I'm not an expert at folder permissions, so there's a strong chance it could be related to that and that I just don't know how to deal with it. You'll see that I did attempt a chmod at the top of the PHP on the uploads/ directory.

我在下面仅包含了PHP代码,没有一个javascript。我不确定JS是否相关,因为它显然可以与实际的块上载配合使用。

I've included only the PHP code below, and none of the javascript. I'm not sure that the JS is relevant since it's apparently working just fine with the actual chunk uploads.

下面的PHP文件与上载/目录位于同一目录/

The PHP file below is in the same directory as the uploads/ folder where the chunks are to end up.

<?php

header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

/* ========================================
  VARIABLES
======================================== */

// chunk variables
$fileId = $_POST['dzuuid'];
$chunkIndex = $_POST['dzchunkindex'] + 1;
$chunkTotal = $_POST['dztotalchunkcount'];

// file path variables
$ds = DIRECTORY_SEPARATOR;
$targetPath = dirname( __FILE__ ) . "{$ds}uploads{$ds}";
$fileType = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
$fileSize = $_FILES["file"]["size"];
$filename = "{$fileId}-{$chunkIndex}.{$fileType}";
$targetFile = $targetPath . $filename;

// change directory permissions
chmod(realpath($targetPath), 0777);

/* ========================================
  DEPENDENCY FUNCTIONS
======================================== */

$returnResponse = function ($info = null, $filelink = null, $status = "error") {
  die (json_encode( array(
    "status" => $status,
    "info" => $info,
    "file_link" => $filelink
  )));
};

/* ========================================
  VALIDATION CHECKS
======================================== */

// I removed all the validation code here. They just prevent upload, so assume the upload is going through.

/* ========================================
  CHUNK UPLOAD
======================================== */

move_uploaded_file($_FILES['file']['tmp_name'], $targetFile);

// Be sure that the file has been uploaded
if ( !file_exists($targetFile) ) $returnResponse("An error occurred and we couldn't upload the requested file.");

/* ========================================
  FINAL UPLOAD CONDITIONAL
======================================== */

if ( $chunkIndex == $chunkTotal ) {

  // ===== concatenate uploaded files =====
  // set emtpy string for file content concatonation
  $file_content = "";
  // loop through temp files and grab the content
  for ($i = 1; $i <= $chunkTotal; $i++) {

    // target temp file
    $temp_file_path = realpath("{$targetPath}{$fileId}-{$i}.{$fileType}") or $returnResponse("Your chunk was lost mid-upload.");
    // ^^^^^^^ this is where the failure is occurring, $i = 1, so first iteration

    // copy chunk...you'll see a bunch of methods included below that I've tried, but the simplest one is method 3, so I've tested mostly there
    // method 1
    /*$temp_file = fopen($temp_file_path, "rb") or $returnResponse("The server cannot open your chunks");
    $chunk = base64_encode(fread($temp_file, $fileSize));
    fclose($temp_file);
    // method 2
    $chunk = base64_encode(stream_get_contents($temp_file_path, $fileSize));*/
    // method 3
    $chunk = base64_encode(file_get_contents($temp_file_path));

    // check chunk content
    if ( empty($chunk) ) $returnResponse("Chunks are uploading as empty strings.");

    // add chunk to main file
    $file_content .= $chunk;

    // delete chunk
    unlink($temp_file_path);
    if ( file_exists($temp_file_path) ) $returnResponse("Your temp files could not be deleted.");

    continue;

  }
  // create and write concatonated chunk to the main file
  file_put_contents("{$targetPath}{$fileId}.{$fileType}", base64_decode($file_content));
// other method of adding contents to new file below, but the one above seemed simpler
  //$final = fopen("{$target_file}.{$fileType}", 'ab');
  //fwrite($final, base64_decode($file_content));
  //fclose($final);

  // create new FileMaker code removed here, irrelevant
// run FileMaker script to populate container field with concatenated file code removed here, irrelevant
// somewhere in the code above, if everything succeeds, I unlink the concatenated file so that it's not cluttering my "uploads" folder, but I never get this far

} else {
  $returnResponse(null, null, "success");
}


推荐答案

出来!问题是我试图在$ chunkIndex == $ chunkTotal时调用串联循环。如果您在浏览器网络监视器中查看,您会经常发现这些块以错误的顺序上传,这会使串联在Realpath步骤中失败,因为该文件实际上还不存在(看起来就像在几秒钟后,我访问了实际的文件夹)。为了证明这一点,只需尝试sleep(5)给其余的时间来上传并查看它是否成功(当然这是一个不好的解决方案,但是是一个快速的测试器)。

I figured it out! The problem is that I was trying to call the concatenation loop when $chunkIndex == $chunkTotal. If you look in the browser network monitor, you'll see that often the chunks upload in the wrong order, which would make the concatenation fail on the realpath step since the file really didn't exist yet (it just looked like it did when I visited the actual folder a few second later). To prove it, just try sleep(5) to give the rest time to upload and see it succeed (of course this is a bad solution, but a quick tester).

解决方案是将上传脚本与串联脚本分开。如果您使用的是Dropzone.js,则可以从 chunksUploaded触发连接脚本,如以下链接所述:

The solution is to separate the upload script from the concatenation script. If you're using Dropzone.js you can trigger the concatenation script from "chunksUploaded," as described at this link:

Dropzone JS-分块

您可以在下面看到最终脚本的外观:

You can see below how the final scripts ended up looking:

script.js

var myDropzone = new Dropzone(target, {
  url: ($(target).attr("action")) ? $(target).attr("action") : "../../chunk-upload.php", // Check that our form has an action attr and if not, set one here
  maxFilesize: 25, // megabytes
  chunking: true,
  parallelUploads: 1,
  parallelChunkUploads: true,
  retryChunks: true,
  retryChunksLimit: 3,
  forceChunking: true,
  chunkSize: 1000000,
  acceptedFiles: "image/*,application/pdf,.doc,.docx,.xls,.xlsx,.csv,.tsv,.ppt,.pptx,.pages,.odt,.rtf,.heif,.hevc",
  previewTemplate: previewTemplate,
  previewsContainer: "#previews",
  clickable: true,
  autoProcessQueue: false,
  chunksUploaded: function(file, done) {
    // All chunks have been uploaded. Perform any other actions
    let currentFile = file;

    // This calls server-side code to merge all chunks for the currentFile
    $.ajax({
        url: "chunk-concat.php?dzuuid=" + currentFile.upload.uuid + "&dztotalchunkcount=" + currentFile.upload.totalChunkCount + "&fileName=" + currentFile.name.substr( (currentFile.name.lastIndexOf('.') +1) ),
        success: function (data) {
            done();
        },
        error: function (msg) {
            currentFile.accepted = false;
            myDropzone._errorProcessing([currentFile], msg.responseText);
        }
     });
  },

});

chunk-upload.php

<?php

/* ========================================
  VARIABLES
======================================== */

// chunk variables
$fileId = $_POST['dzuuid'];
$chunkIndex = $_POST['dzchunkindex'] + 1;
$chunkTotal = $_POST['dztotalchunkcount'];

// file path variables
$ds = DIRECTORY_SEPARATOR;
$targetPath = dirname( __FILE__ ) . "{$ds}uploads{$ds}";
$fileType = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
$fileSize = $_FILES["file"]["size"];
$filename = "{$fileId}-{$chunkIndex}.{$fileType}";
$targetFile = $targetPath . $filename;

// change directory permissions
chmod(realpath($targetPath), 0777) or die("Could not modify directory permissions.");

/* ========================================
  DEPENDENCY FUNCTIONS
======================================== */

$returnResponse = function ($info = null, $filelink = null, $status = "error") {
  die (json_encode( array(
    "status" => $status,
    "info" => $info,
    "file_link" => $filelink
  )));
};

/* ========================================
  VALIDATION CHECKS
======================================== */

// blah, blah, blah validation stuff goes here

/* ========================================
  CHUNK UPLOAD
======================================== */

move_uploaded_file($_FILES['file']['tmp_name'], $targetFile);

// Be sure that the file has been uploaded
if ( !file_exists($targetFile) ) $returnResponse("An error occurred and we couldn't upload the requested file.");
chmod($targetFile, 0777) or $returnResponse("Could not reset permissions on uploaded chunk.");

$returnResponse(null, null, "success");

chunk-concat.php

<?php

// get variables
$fileId = $_GET['dzuuid'];
$chunkTotal = $_GET['dztotalchunkcount'];

// file path variables
$ds = DIRECTORY_SEPARATOR;
$targetPath = dirname( __FILE__ ) . "{$ds}uploads{$ds}";
$fileType = $_GET['fileName'];

/* ========================================
  DEPENDENCY FUNCTIONS
======================================== */

$returnResponse = function ($info = null, $filelink = null, $status = "error") {
  die (json_encode( array(
    "status" => $status,
    "info" => $info,
    "file_link" => $filelink
  )));
};

/* ========================================
  CONCATENATE UPLOADED FILES
======================================== */

// loop through temp files and grab the content
for ($i = 1; $i <= $chunkTotal; $i++) {

  // target temp file
  $temp_file_path = realpath("{$targetPath}{$fileId}-{$i}.{$fileType}") or $returnResponse("Your chunk was lost mid-upload.");

  // copy chunk
  $chunk = file_get_contents($temp_file_path);
  if ( empty($chunk) ) $returnResponse("Chunks are uploading as empty strings.");

  // add chunk to main file
  file_put_contents("{$targetPath}{$fileId}.{$fileType}", $chunk, FILE_APPEND | LOCK_EX);

  // delete chunk
  unlink($temp_file_path);
  if ( file_exists($temp_file_path) ) $returnResponse("Your temp files could not be deleted.");

}

/* ========== a bunch of steps I removed below here because they're irrelevant, but I described them anyway ========== */
// create FileMaker record
// run FileMaker script to populate container field with newly-created file
// unlink newly created file
// return success

这篇关于如何使用PHP连接从Dropzone.js上传的分块文件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆