tripal.upload.inc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. <?php
  2. function tripal_file_upload($type, $filename, $action = NULL, $chunk = 0) {
  3. global $user;
  4. $user_dir = 'public://tripal/users/' . $user->uid;
  5. if (!file_prepare_directory($user_dir, FILE_CREATE_DIRECTORY)) {
  6. $message = 'Could not access the directory on the server for storing this file.';
  7. watchdog('tripal', $message, array(), WATCHDOG_ERROR);
  8. drupal_json_output(array(
  9. 'status' => 'failed',
  10. 'message' => $message,
  11. 'file_id' => '',
  12. ));
  13. return;
  14. }
  15. // Check to see if this file is uploaded already. If it is
  16. // we don't want to allow the file to be uploaded again.
  17. // $merge_file = $user_dir . '/' . $filename;
  18. // if (file_exists($merge_file)) {
  19. // $message = 'This file aready exists on the server and will not be ' .
  20. // 'uploaded again.',
  21. // watchdog('tripal', $message, array(), WATCHDOG_ERROR);
  22. // drupal_json_output(array(
  23. // 'status' => 'failed',
  24. // 'message' => $message,
  25. // 'file_id' => '',
  26. // ));
  27. // return;
  28. // }
  29. switch ($action) {
  30. // If the action is 'put' then the callee is sending a chunk of the file
  31. case 'save':
  32. tripal_file_upload_put($filename, $chunk, $user_dir);
  33. break;
  34. case 'check':
  35. tripal_file_upload_check($filename, $chunk, $user_dir);
  36. break;
  37. case 'merge':
  38. tripal_file_upload_merge($filename, $type, $user_dir);
  39. break;
  40. }
  41. }
  42. /**
  43. * Merges all chunks into a single file
  44. * @param unknown $filename
  45. */
  46. function tripal_file_upload_merge($filename, $type, $user_dir) {
  47. global $user;
  48. $module = $_GET['module'];
  49. $status = 'merging';
  50. $message = '';
  51. // Build the paths to the temp directory and merged file.
  52. $temp_dir = $user_dir . '/temp' . '/' . $filename;
  53. $merge_file = $user_dir . '/' . $filename;
  54. // If the temp directory where the chunks are found does not exist and the
  55. // client is requesting merge then most likely the file has already been
  56. // merged and the user hit the upload button again.
  57. if (file_exists($temp_dir)) {
  58. // Get the upload log.
  59. $log = tripal_file_upload_read_log($temp_dir);
  60. // Keep up with the expected file size.
  61. $merge_size = 0;
  62. // Open the new merged file.
  63. $merge_fh = fopen($merge_file, "w");
  64. if ($merge_fh){
  65. if (flock($merge_fh, LOCK_EX)) {
  66. $chunks_written = $log['chunks_written'];
  67. $max_chunk = max(array_keys($chunks_written));
  68. // Iterate through the chunks in order and see if any are missing.
  69. // If so then break out of the loop and fail. Otherwise concatentate
  70. // them together.
  71. for ($i = 0; $i <= $max_chunk; $i++) {
  72. if (!array_key_exists($i, $chunks_written)) {
  73. $status = 'failed';
  74. $message = 'Missing some chunks. Cannot complete file merge.';
  75. break;
  76. }
  77. $merge_size += $chunks_written[$i];
  78. $chunk_file = $temp_dir . '/' . $filename . '.chunk.' . $i;
  79. $cfh = fopen($chunk_file, 'r');
  80. while ($data = fread($cfh, 1024)) {
  81. fwrite($merge_fh, $data);
  82. }
  83. fclose($cfh);
  84. } // end for ($i = 0; $i <= $max_chunk; $i++) { ...
  85. if (filesize($merge_file) != $merge_size) {
  86. $status = 'failed';
  87. $message = 'File was uploaded but final merged size is incorrect: ' . $merge_file . '.';
  88. }
  89. }
  90. else {
  91. $status = 'failed';
  92. $message = 'Cannot lock merged file for writing: ' . $merge_file . '.';
  93. }
  94. }
  95. else {
  96. $status = 'failed';
  97. $message = 'Cannot open merged file: ' . $merge_file . '.';
  98. }
  99. fclose($merge_fh);
  100. }
  101. // Make sure the merged file exists
  102. if (!file_exists($merge_file)) {
  103. $status = 'failed';
  104. $message = 'Merge file is missing after upload ' . $merge_file . '.';
  105. }
  106. $file_id = NULL;
  107. // If the file has been successfully merged then let the calling module
  108. // deal with it.
  109. if ($status != 'failed') {
  110. $function = $module . '_handle_uploaded_file';
  111. if(function_exists($function)) {
  112. $file_id = $function($filename, $merge_file, $type);
  113. if ($file_id) {
  114. $file = file_load($file_id);
  115. $status = 'completed';
  116. $full_path = drupal_realpath($file->uri);
  117. $md5sum = md5_file($full_path);
  118. $md5sum_file = fopen("$full_path.md5", "w");
  119. fwrite($md5sum_file, $md5sum);
  120. fclose($md5sum_file);
  121. unlink($temp_dir);
  122. }
  123. else {
  124. $status = 'failed';
  125. $message = 'Could not add file to the database.';
  126. }
  127. }
  128. else {
  129. $status = 'failed';
  130. $message = 'Cannot find the function: ' . $function . '().';
  131. }
  132. }
  133. if ($status == 'failed') {
  134. watchdog('tripal', $message, array(), WATCHDOG_ERROR);
  135. }
  136. drupal_json_output(array(
  137. 'status' => $status,
  138. 'message' => $message,
  139. 'file_id' => $file_id,
  140. ));
  141. }
  142. /**
  143. * Checks the size of a chunk to see if is fully uploded.
  144. *
  145. * @return
  146. * returns a JSON array with a status, message and the
  147. * current chunk.
  148. */
  149. function tripal_file_upload_check($filename, $chunk, $user_dir) {
  150. $chunk_size = $_GET['chunk_size'];
  151. // Store the chunked file in a temp folder.
  152. $temp_dir = $user_dir . '/temp' . '/' . $filename;
  153. if (!file_exists($temp_dir)) {
  154. mkdir($temp_dir, 0700, TRUE);
  155. }
  156. // Get the upload log.
  157. $log = tripal_file_upload_read_log($temp_dir);
  158. $chunks_written = $log['chunks_written'];
  159. $max_chunk = 0;
  160. if ($chunks_written) {
  161. $max_chunk = max(array_keys($chunks_written));
  162. }
  163. // Iterate through the chunks in order and see if any are missing.
  164. // If so then break out of the loop and this is the chunk to start
  165. // on.
  166. for ($i = 0; $i <= $max_chunk; $i++) {
  167. if (!array_key_exists($i, $chunks_written)) {
  168. break;
  169. }
  170. }
  171. drupal_json_output(array(
  172. 'status' => 'success',
  173. 'message' => '',
  174. 'curr_chunk' => $i,
  175. ));
  176. }
  177. /**
  178. * Saves the contents of the file being sent to the server.
  179. *
  180. * The file is saved using the filename the chunk number as an
  181. * extension. This function uses file locking to prevent two
  182. * jobs from writing to the same file at the same time.
  183. */
  184. function tripal_file_upload_put($filename, $chunk, $user_dir) {
  185. // Get the HTTP PUT data.
  186. $putdata = fopen("php://input", "r");
  187. $size = $_SERVER['CONTENT_LENGTH'];
  188. // Store the chunked file in a temp folder.
  189. $temp_dir = $user_dir . '/temp/' . $filename;
  190. if (!file_exists($temp_dir)) {
  191. mkdir($temp_dir, 0700, TRUE);
  192. }
  193. // Open the file for writing if doesn't already exist with the proper size.
  194. $chunk_file = $temp_dir . '/' . $filename . '.chunk.' . $chunk;
  195. if (!file_exists($chunk_file) or filesize($chunk_file) != $size) {
  196. // Read the data 1 KB at a time and write to the file
  197. $fh = fopen($chunk_file, "w");
  198. // Lock the file for writing. We don't want two different
  199. // processes trying to write to the same file at the same time.
  200. if (flock($fh, LOCK_EX)) {
  201. while ($data = fread($putdata, 1024)) {
  202. fwrite($fh, $data);
  203. }
  204. fclose($fh);
  205. }
  206. }
  207. fclose($putdata);
  208. // Get the current log, updated and re-write it.
  209. $log = tripal_file_upload_read_log($temp_dir);
  210. $log['chunks_written'][$chunk] = $size;
  211. tripal_file_upoad_write_log($temp_dir, $log);
  212. }
  213. /**
  214. * Reads the upload log file.
  215. *
  216. * The log file is used to keep track of which chunks have been uploaded.
  217. * The format is an array with a key of 'chunks_written' which each element
  218. * a key/value pair containing the chunk index as the key and the chunk size
  219. * as the value.
  220. *
  221. * @param $temp_dir
  222. * The directory where the log file will be written. It must be a unique
  223. * directory where only chunks from a single file are kept.
  224. */
  225. function tripal_file_upload_read_log($temp_dir) {
  226. $log_file = $temp_dir . '/tripal_upload.log';
  227. $log = NULL;
  228. if (file_exists($log_file)) {
  229. $fh = fopen($log_file, "r");
  230. if ($fh and flock($fh, LOCK_EX)) {
  231. $contents = '';
  232. while ($data = fread($fh, 1024)) {
  233. $contents .= $data;
  234. }
  235. $log = unserialize($contents);
  236. }
  237. fclose($fh);
  238. }
  239. if (!is_array($log)) {
  240. $log = array(
  241. 'chunks_written' => array(),
  242. );
  243. }
  244. return $log;
  245. }
  246. /**
  247. * Writes the upload log file.
  248. *
  249. * The log file is used to keep track of which chunks have been uploaded.
  250. * The format is an array with a key of 'chunks_written' which each element
  251. * a key/value pair containing the chunk index as the key and the chunk size
  252. * as the value.
  253. *
  254. * @param $temp_dir
  255. * The directory where the log file will be written. It must be a unique
  256. * directory where only chunks from a single file are kept.
  257. * @param $log
  258. * The log array, that is serialized and then written to the file.
  259. */
  260. function tripal_file_upoad_write_log($temp_dir, $log) {
  261. $log_file = $temp_dir . '/tripal_upload.log';
  262. if (!$log or !is_array($log)) {
  263. $log = array(
  264. 'chunks_written' => array(),
  265. );
  266. }
  267. // Get the last chunk read
  268. $fh = fopen($log_file, "w");
  269. if ($fh and flock($fh, LOCK_EX)) {
  270. fwrite($fh, serialize($log));
  271. }
  272. fclose($fh);
  273. }