douzhuiqing1151 2019-05-15 19:28
浏览 149
已采纳

如何使用PHP连接Dropzone.js中的chunked文件上传?

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.

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).

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.

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.

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.

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");
}

  • 写回答

1条回答 默认 最新

  • duanjiangzhi6851 2019-05-16 21:53
    关注

    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).

    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 - Chunking

    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
    
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 Attention is all you need 的代码运行
  • ¥15 一个服务器已经有一个系统了如果用usb再装一个系统,原来的系统会被覆盖掉吗
  • ¥15 使用esm_msa1_t12_100M_UR50S蛋白质语言模型进行零样本预测时,终端显示出了sequence handled的进度条,但是并不出结果就自动终止回到命令提示行了是怎么回事:
  • ¥15 前置放大电路与功率放大电路相连放大倍数出现问题
  • ¥30 关于<main>标签页面跳转的问题
  • ¥80 部署运行web自动化项目
  • ¥15 腾讯云如何建立同一个项目中物模型之间的联系
  • ¥30 VMware 云桌面水印如何添加
  • ¥15 用ns3仿真出5G核心网网元
  • ¥15 matlab答疑 关于海上风电的爬坡事件检测