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

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

报告相同问题?

悬赏问题

  • ¥18 深度学习tensorflow1,ssdv1,coco数据集训练一个模型
  • ¥100 关于注册表摄像头和麦克风的问题
  • ¥30 代码本地运行正常,但是TOMCAT部署时闪退
  • ¥15 关于#python#的问题
  • ¥15 主机可以ping通路由器但是连不上网怎么办
  • ¥15 数据库一张以时间排好序的表中,找出多次相邻的那些行
  • ¥50 关于DynamoRIO处理多线程程序时候的问题
  • ¥15 kubeadm部署k8s出错
  • ¥15 Abaqus打不开cae文件怎么办?
  • ¥15 小程序准备上线,软件开发公司需要提供哪些资料给甲方