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 运筹学排序问题中的在线排序
  • ¥15 关于docker部署flink集成hadoop的yarn,请教个问题 flink启动yarn-session.sh连不上hadoop,这个整了好几天一直不行,求帮忙看一下怎么解决
  • ¥30 求一段fortran代码用IVF编译运行的结果
  • ¥15 深度学习根据CNN网络模型,搭建BP模型并训练MNIST数据集
  • ¥15 C++ 头文件/宏冲突问题解决
  • ¥15 用comsol模拟大气湍流通过底部加热(温度不同)的腔体
  • ¥50 安卓adb backup备份子用户应用数据失败
  • ¥20 有人能用聚类分析帮我分析一下文本内容嘛
  • ¥30 python代码,帮调试,帮帮忙吧
  • ¥15 #MATLAB仿真#车辆换道路径规划