123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- <?php
- function tripal_file_upload($type, $filename, $action = NULL, $chunk = 0) {
- global $user;
- $user_dir = 'public://tripal/users/' . $user->uid;
- if (!file_prepare_directory($user_dir, FILE_CREATE_DIRECTORY)) {
- $message = 'Could not access the directory on the server for storing this file.';
- watchdog('tripal', $message, array(), WATCHDOG_ERROR);
- drupal_json_output(array(
- 'status' => 'failed',
- 'message' => $message,
- 'file_id' => '',
- ));
- return;
- }
- // Check to see if this file is uploaded already. If it is
- // we don't want to allow the file to be uploaded again.
- // $merge_file = $user_dir . '/' . $filename;
- // if (file_exists($merge_file)) {
- // $message = 'This file aready exists on the server and will not be ' .
- // 'uploaded again.',
- // watchdog('tripal', $message, array(), WATCHDOG_ERROR);
- // drupal_json_output(array(
- // 'status' => 'failed',
- // 'message' => $message,
- // 'file_id' => '',
- // ));
- // return;
- // }
- switch ($action) {
- // If the action is 'put' then the callee is sending a chunk of the file
- case 'save':
- tripal_file_upload_put($filename, $chunk, $user_dir);
- break;
- case 'check':
- tripal_file_upload_check($filename, $chunk, $user_dir);
- break;
- case 'merge':
- tripal_file_upload_merge($filename, $type, $user_dir);
- break;
- }
- }
- /**
- * Merges all chunks into a single file
- * @param unknown $filename
- */
- function tripal_file_upload_merge($filename, $type, $user_dir) {
- global $user;
- $module = $_GET['module'];
- $status = 'merging';
- $message = '';
- // Build the paths to the temp directory and merged file.
- $temp_dir = $user_dir . '/temp' . '/' . $filename;
- $merge_file = $user_dir . '/' . $filename;
- // If the temp directory where the chunks are found does not exist and the
- // client is requesting merge then most likely the file has already been
- // merged and the user hit the upload button again.
- if (file_exists($temp_dir)) {
- // Get the upload log.
- $log = tripal_file_upload_read_log($temp_dir);
- // Keep up with the expected file size.
- $merge_size = 0;
- // Open the new merged file.
- $merge_fh = fopen($merge_file, "w");
- if ($merge_fh){
- if (flock($merge_fh, LOCK_EX)) {
- $chunks_written = $log['chunks_written'];
- $max_chunk = max(array_keys($chunks_written));
- // Iterate through the chunks in order and see if any are missing.
- // If so then break out of the loop and fail. Otherwise concatentate
- // them together.
- for ($i = 0; $i <= $max_chunk; $i++) {
- if (!array_key_exists($i, $chunks_written)) {
- $status = 'failed';
- $message = 'Missing some chunks. Cannot complete file merge.';
- break;
- }
- $merge_size += $chunks_written[$i];
- $chunk_file = $temp_dir . '/' . $filename . '.chunk.' . $i;
- $cfh = fopen($chunk_file, 'r');
- while ($data = fread($cfh, 1024)) {
- fwrite($merge_fh, $data);
- }
- fclose($cfh);
- } // end for ($i = 0; $i <= $max_chunk; $i++) { ...
- if (filesize($merge_file) != $merge_size) {
- $status = 'failed';
- $message = 'File was uploaded but final merged size is incorrect: ' . $merge_file . '.';
- }
- }
- else {
- $status = 'failed';
- $message = 'Cannot lock merged file for writing: ' . $merge_file . '.';
- }
- }
- else {
- $status = 'failed';
- $message = 'Cannot open merged file: ' . $merge_file . '.';
- }
- fclose($merge_fh);
- }
- // Make sure the merged file exists
- if (!file_exists($merge_file)) {
- $status = 'failed';
- $message = 'Merge file is missing after upload ' . $merge_file . '.';
- }
- $file_id = NULL;
- // If the file has been successfully merged then let the calling module
- // deal with it.
- if ($status != 'failed') {
- $function = $module . '_handle_uploaded_file';
- if(function_exists($function)) {
- $file_id = $function($filename, $merge_file, $type);
- if ($file_id) {
- $file = file_load($file_id);
- $status = 'completed';
- $full_path = drupal_realpath($file->uri);
- $md5sum = md5_file($full_path);
- $md5sum_file = fopen("$full_path.md5", "w");
- fwrite($md5sum_file, $md5sum);
- fclose($md5sum_file);
- unlink($temp_dir);
- }
- else {
- $status = 'failed';
- $message = 'Could not add file to the database.';
- }
- }
- else {
- $status = 'failed';
- $message = 'Cannot find the function: ' . $function . '().';
- }
- }
- if ($status == 'failed') {
- watchdog('tripal', $message, array(), WATCHDOG_ERROR);
- }
- drupal_json_output(array(
- 'status' => $status,
- 'message' => $message,
- 'file_id' => $file_id,
- ));
- }
- /**
- * Checks the size of a chunk to see if is fully uploded.
- *
- * @return
- * returns a JSON array with a status, message and the
- * current chunk.
- */
- function tripal_file_upload_check($filename, $chunk, $user_dir) {
- $chunk_size = $_GET['chunk_size'];
- // Store the chunked file in a temp folder.
- $temp_dir = $user_dir . '/temp' . '/' . $filename;
- if (!file_exists($temp_dir)) {
- mkdir($temp_dir, 0700, TRUE);
- }
- // Get the upload log.
- $log = tripal_file_upload_read_log($temp_dir);
- $chunks_written = $log['chunks_written'];
- $max_chunk = 0;
- if ($chunks_written) {
- $max_chunk = max(array_keys($chunks_written));
- }
- // Iterate through the chunks in order and see if any are missing.
- // If so then break out of the loop and this is the chunk to start
- // on.
- for ($i = 0; $i <= $max_chunk; $i++) {
- if (!array_key_exists($i, $chunks_written)) {
- break;
- }
- }
- drupal_json_output(array(
- 'status' => 'success',
- 'message' => '',
- 'curr_chunk' => $i,
- ));
- }
- /**
- * Saves the contents of the file being sent to the server.
- *
- * The file is saved using the filename the chunk number as an
- * extension. This function uses file locking to prevent two
- * jobs from writing to the same file at the same time.
- */
- function tripal_file_upload_put($filename, $chunk, $user_dir) {
- // Get the HTTP PUT data.
- $putdata = fopen("php://input", "r");
- $size = $_SERVER['CONTENT_LENGTH'];
- // Store the chunked file in a temp folder.
- $temp_dir = $user_dir . '/temp/' . $filename;
- if (!file_exists($temp_dir)) {
- mkdir($temp_dir, 0700, TRUE);
- }
- // Open the file for writing if doesn't already exist with the proper size.
- $chunk_file = $temp_dir . '/' . $filename . '.chunk.' . $chunk;
- if (!file_exists($chunk_file) or filesize($chunk_file) != $size) {
- // Read the data 1 KB at a time and write to the file
- $fh = fopen($chunk_file, "w");
- // Lock the file for writing. We don't want two different
- // processes trying to write to the same file at the same time.
- if (flock($fh, LOCK_EX)) {
- while ($data = fread($putdata, 1024)) {
- fwrite($fh, $data);
- }
- fclose($fh);
- }
- }
- fclose($putdata);
- // Get the current log, updated and re-write it.
- $log = tripal_file_upload_read_log($temp_dir);
- $log['chunks_written'][$chunk] = $size;
- tripal_file_upoad_write_log($temp_dir, $log);
- }
- /**
- * Reads the upload log file.
- *
- * The log file is used to keep track of which chunks have been uploaded.
- * The format is an array with a key of 'chunks_written' which each element
- * a key/value pair containing the chunk index as the key and the chunk size
- * as the value.
- *
- * @param $temp_dir
- * The directory where the log file will be written. It must be a unique
- * directory where only chunks from a single file are kept.
- */
- function tripal_file_upload_read_log($temp_dir) {
- $log_file = $temp_dir . '/tripal_upload.log';
- $log = NULL;
- if (file_exists($log_file)) {
- $fh = fopen($log_file, "r");
- if ($fh and flock($fh, LOCK_EX)) {
- $contents = '';
- while ($data = fread($fh, 1024)) {
- $contents .= $data;
- }
- $log = unserialize($contents);
- }
- fclose($fh);
- }
- if (!is_array($log)) {
- $log = array(
- 'chunks_written' => array(),
- );
- }
- return $log;
- }
- /**
- * Writes the upload log file.
- *
- * The log file is used to keep track of which chunks have been uploaded.
- * The format is an array with a key of 'chunks_written' which each element
- * a key/value pair containing the chunk index as the key and the chunk size
- * as the value.
- *
- * @param $temp_dir
- * The directory where the log file will be written. It must be a unique
- * directory where only chunks from a single file are kept.
- * @param $log
- * The log array, that is serialized and then written to the file.
- */
- function tripal_file_upoad_write_log($temp_dir, $log) {
- $log_file = $temp_dir . '/tripal_upload.log';
- if (!$log or !is_array($log)) {
- $log = array(
- 'chunks_written' => array(),
- );
- }
- // Get the last chunk read
- $fh = fopen($log_file, "w");
- if ($fh and flock($fh, LOCK_EX)) {
- fwrite($fh, serialize($log));
- }
- fclose($fh);
- }
|