phpchunksplupload

plupload php large size video upload chunk issue


I am using plupload for uploading large / big size video files with another form fields. I am currently developing site on WAMP server on windows 11.

I am facing issue :

Chunk upload works but files uploaded are not getting re-joined. so multiple uploaded files like myvideo.mp4.part are getting saved in upload folder. (e.g. if uploading myvideo.mp4 of 75 MB, in upload folder i found multiple myvideo.mp4.part files with different - different sizes .Image provided below)

Providing coding I am trying...

Additional Settings Tried :

I updated upload_max_filesize to 2000 M and post_max_size to 2000 M in php.ini, restarted Apache and tried. Also tried adding these settings in video-upload.php script. But till issue is there.

Current Code :

index.php

<?php
include("db.php");

if ((isset($_POST['action'])) && ($_POST['action']=="add-video")){

$lecturer_name   = $database->filter($_POST["lecturer_name"]);
$lecturer_mobile = $database->filter($_POST["lecturer_mobile"]);
$lecturer_email  = $database->filter($_POST["lecturer_email"]);
$video_url       = $database->filter($_POST["video_url"]);

/* Rest PHP Validation And Insert Data in Mysql Database Table Code Here */

}    
?>
<!DOCTYPE html>
 <html>
  <head>
    
     <script src="js/jquery.min.js"></script>
     <link type="text/css" rel="stylesheet" href="src/plugins/plupload/js/jquery.ui.plupload/css/jquery.ui.plupload.css" media="screen" />
     <link type="text/css" rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/themes/smoothness/jquery-ui.min.css" media="screen" />
     <script src="src/plugins/plupload/js/plupload.full.min.js"></script>
     <script type="text/javascript" src="src/plugins/plupload/js/jquery.ui.plupload/jquery.ui.plupload.min.js" charset="UTF-8"></script>
     <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js" charset="UTF-8"></script>
     <script src="src/plugins/plupload/jquery.plupload.queue.js"></script>

     <script type="text/javascript">
     // Initialize the widget when the DOM is ready
     $(function() {
        $("#uploader").plupload({
        // General settings
           runtimes : 'html5,flash,silverlight,html4',
           url : "video-upload.php",

         // Maximum file size
          max_file_size : '999mb',

          chunk_size: '1mb',

     // Specify what files to browse for
    filters : [
          {title : "Image files", extensions : "jpg,jpeg,gif,png"},
          {title : "Video files", extensions : "mp4,avi,mpeg,mpg,mov,wmv"},
          {title : "Zip files", extensions : "zip"},
          {title : "Document files", extensions : "pdf,docx,xlsx"}
    ],

    // Rename files by clicking on their titles
    rename: true,
     
    // Sort files
    sortable: true,

    // Enable ability to drag'n'drop files onto the widget (currently only HTML5 supports that)
    dragdrop: true,

    // Views to activate
    views: {
        list: true,
        thumbs: true, // Show thumbs
        active: 'thumbs'
    },

    // Flash settings
    flash_swf_url : 'src/plugins/plupload/js/Moxie.swf',
 
    // Silverlight settings
    silverlight_xap_url : 'src/plugins/plupload/js/Moxie.xap',
    
    init : {
         FileUploaded: function(up, file, info) {
            $("#video_url").val(file.name);
         }                   
     }
 });    
});
</script>

  </head>
  <body>

   <form id="main-group-addition" action="" method="post" enctype="multipart/form-data">
       <input type="hidden" name="action" value="add-video" />
       <div class="form-group">
                <label>Lecturer Name *</label>
                    <input type="text" name="lecturer_name" class="form-control" id="lecturer_name" value="<?php if(isset($lecturer_name)){ echo $lecturer_name;}?>" >
       </div>
       <div class="form-group">
                <label>Lecturer Mobile *</label>
                    <input type="text" name="lecturer_mobile" class="form-control" id="lecturer_mobile" value="<?php if(isset($lecturer_mobile)){ echo $lecturer_mobile;}?>" >
       </div>
       <div class="form-group">
                <label>Lecturer Email *</label>
                    <input type="text" name="lecturer_email" class="form-control" id="lecturer_email" value="<?php if(isset($lecturer_email)){ echo $lecturer_email;}?>" >
       </div>

      <!-- Video Upload Div Used With plupload. First Video Uploaded And File Name is Inserted via above Javascript Bind option in Below "video_url" input field ->
       <div class="form-group"> 
          <div id="uploader">
            <p>Your browser doesn't have Flash, Silverlight or HTML5 support.</p>
          </div>
       </div>
       
       <div class="form-group">
                <label>Video URL *</label>
                    <input type="text" name="video_url" class="form-control" id="video_url" value="<?php if(isset($video_url)){ echo $video_url;}?>" readonly>
       </div>


       <div class="form-group">
            <input type="submit" name="mysubmit1" id="mysubmit2" value="Save" class="btn btn-danger">
        </div>

   </form>


  </body>
  </html>

video-upload.php

<?php
include('db.php');

// changing the upload limits, Also changed these values in php.ini file and restarted Apache
 ini_set('upload_max_filesize', '2000M');
 ini_set('post_max_size', '2000M');
 ini_set('max_input_time', 600);
 ini_set('max_execution_time', 600);

 // Make sure file is not cached (as it happens for example on iOS devices) 
 header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); 
 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); 
 header("Cache-Control: no-store, no-cache, must-revalidate"); 
 header("Cache-Control: post-check=0, pre-check=0", false); 
 header("Pragma: no-cache"); 

// Settings 
$targetDir = '../uploads/video/'; 
$cleanupTargetDir = true; // Remove old files 
$maxFileAge = 5 * 3600; // Temp file age in seconds 


// Create target dir 
if (!file_exists($targetDir)) { 
  @mkdir($targetDir); 
} 

// Get a file name ^^^ RENAMING FILE NAME HERE ^^^

$udfile = date("YmdHis");

if (isset($_REQUEST["name"])) { 
    $fileName = $udfile.'-'.$_REQUEST["name"]; 
} elseif (!empty($_FILES)) { 
    $fileName = $udfile.'-'.$_FILES["file"]["name"]; 
} else { 
    $fileName = $udfile; 
} 

$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName; 

// Chunking might be enabled 
$chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0; 
$chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0; 


// Remove old temp files     
if ($cleanupTargetDir) { 
  if (!is_dir($targetDir) || !$dir = opendir($targetDir)) { 
    die('{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}'); 
 } 

 while (($file = readdir($dir)) !== false) { 
    $tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file; 

    // If temp file is current file proceed to the next 
    if ($tmpfilePath == "{$filePath}.part") { 
        continue; 
    } 

    // Remove temp file if it is older than the max age and is not the current file 
    if (preg_match('/\.part$/', $file) && (filemtime($tmpfilePath) < time() - $maxFileAge)) { 
        @unlink($tmpfilePath); 
    } 
  } 
closedir($dir); 
}     


 // Open temp file 
 if (!$out = @fopen("{$filePath}.part", $chunks ? "ab" : "wb")) { 
die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}'); 
 } 

 if (!empty($_FILES)) { 
    if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) { 
    die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}'); 
 } 

// Read binary input stream and append it to temp file 
if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb")) { 
    die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}'); 
} 
} else {     
if (!$in = @fopen("php://input", "rb")) { 
    die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}'); 
} 
} 

while ($buff = fread($in, 4096)) { 
   fwrite($out, $buff); 
} 

@fclose($out); 
@fclose($in); 

// Check if file has been uploaded 
if (!$chunks || $chunk == $chunks - 1) { 
   // Strip the temp .part suffix off  
    rename("{$filePath}.part", $filePath); 
} 

  // Return Success JSON-RPC response 

 die('{"jsonrpc" : "2.0", "result" : {"cleanFileName": "'.$fileName.'"}, "id" : "id"}');
?>

Uploaded files get saved in several part files :

IMAGE OF CHINKED FILES IN PARTS IN UPLOAD FOLDER

What is the solution for issue - Chunk upload works but files uploaded are not getting re-joined. so multiple uploaded files like myvideo.mp4.part are getting saved in upload folder.


Solution

  • This happens on a local WAMP environment (Windows 11). My php.ini settings are updated to support large uploads (upload_max_filesize = 2000M, post_max_size = 2000M), and I’ve confirmed chunking is active in the client-side Plupload config.


    Problem

    In my video-upload.php script, I'm dynamically changing the filename like this:

    $udfile = date("YmdHis");
    
    if (isset($_REQUEST["name"])) { 
        $fileName = $udfile . '-' . $_REQUEST["name"]; 
    }
    

    This causes each chunk to be saved with a different name, so Plupload cannot append chunks to a single .part file. As a result, no final file is assembled and only .part fragments remain.


    Solution

    Do not modify the filename per request. Plupload handles chunk naming correctly as long as you use a consistent name.

    Replace:

    $udfile = date("YmdHis");
    
    if (isset($_REQUEST["name"])) { 
        $fileName = $udfile . '-' . $_REQUEST["name"]; 
    }
    

    With:

    if (isset($_REQUEST["name"])) { 
        $fileName = $_REQUEST["name"]; 
    }
    

    Let Plupload assign the file name. If you need to rename the final file for uniqueness, do that only after the last chunk has been received.

    Correct Chunk Handling Example

    $targetDir = '../uploads/video/';
    $fileName = isset($_REQUEST["name"]) ? $_REQUEST["name"] : 'unnamed';
    $filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;
    $tempFilePath = $filePath . '.part';
    
    $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
    $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0;
    
    // Open temp file
    $out = @fopen($tempFilePath, $chunks ? "ab" : "wb");
    
    if (!empty($_FILES)) {
        $in = @fopen($_FILES["file"]["tmp_name"], "rb");
    } else {
        $in = @fopen("php://input", "rb");
    }
    
    // Write chunks to .part file
    while ($buff = fread($in, 4096)) {
        fwrite($out, $buff);
    }
    @fclose($in);
    @fclose($out);
    
    // Finalize: rename only if last chunk
    if (!$chunks || $chunk == $chunks - 1) {
        rename($tempFilePath, $filePath);
    }
    

    Summary

    ✅ Do This ❌ Don’t Do This
    Use $_REQUEST["name"] directly Rename file with date() for each chunk
    Use "ab" mode for appending Overwrite with "wb" in chunked mode
    Rename final file after last chunk Rename during each chunk upload

    Extra Tips