filechunking.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <?php
  2. /**
  3. * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
  4. * This file is licensed under the Affero General Public License version 3 or
  5. * later.
  6. * See the COPYING-README file.
  7. */
  8. class OC_FileChunking {
  9. protected $info;
  10. protected $cache;
  11. static public function decodeName($name) {
  12. preg_match('/(?P<name>.*)-chunking-(?P<transferid>\d+)-(?P<chunkcount>\d+)-(?P<index>\d+)/', $name, $matches);
  13. return $matches;
  14. }
  15. public function __construct($info) {
  16. $this->info = $info;
  17. }
  18. public function getPrefix() {
  19. $name = $this->info['name'];
  20. $transferid = $this->info['transferid'];
  21. return $name.'-chunking-'.$transferid.'-';
  22. }
  23. protected function getCache() {
  24. if (!isset($this->cache)) {
  25. $this->cache = new \OC\Cache\File();
  26. }
  27. return $this->cache;
  28. }
  29. /**
  30. * Stores the given $data under the given $key - the number of stored bytes is returned
  31. *
  32. * @param $index
  33. * @param $data
  34. * @return int
  35. */
  36. public function store($index, $data) {
  37. $cache = $this->getCache();
  38. $name = $this->getPrefix().$index;
  39. $cache->set($name, $data);
  40. return $cache->size($name);
  41. }
  42. public function isComplete() {
  43. $prefix = $this->getPrefix();
  44. $parts = 0;
  45. $cache = $this->getCache();
  46. for($i=0; $i < $this->info['chunkcount']; $i++) {
  47. if ($cache->hasKey($prefix.$i)) {
  48. $parts ++;
  49. }
  50. }
  51. return $parts == $this->info['chunkcount'];
  52. }
  53. public function assemble($f) {
  54. $cache = $this->getCache();
  55. $prefix = $this->getPrefix();
  56. $count = 0;
  57. for($i=0; $i < $this->info['chunkcount']; $i++) {
  58. $chunk = $cache->get($prefix.$i);
  59. $count += fwrite($f, $chunk);
  60. }
  61. $this->cleanup();
  62. return $count;
  63. }
  64. /**
  65. * Removes all chunks which belong to this transmission
  66. */
  67. public function cleanup() {
  68. $cache = $this->getCache();
  69. $prefix = $this->getPrefix();
  70. for($i=0; $i < $this->info['chunkcount']; $i++) {
  71. $cache->remove($prefix.$i);
  72. }
  73. }
  74. /**
  75. * Removes one specific chunk
  76. * @param $index
  77. */
  78. public function remove($index) {
  79. $cache = $this->getCache();
  80. $prefix = $this->getPrefix();
  81. $cache->remove($prefix.$index);
  82. }
  83. public function signature_split($orgfile, $input) {
  84. $info = unpack('n', fread($input, 2));
  85. $blocksize = $info[1];
  86. $this->info['transferid'] = mt_rand();
  87. $count = 0;
  88. $needed = array();
  89. $cache = $this->getCache();
  90. $prefix = $this->getPrefix();
  91. while (!feof($orgfile)) {
  92. $new_md5 = fread($input, 16);
  93. if (feof($input)) {
  94. break;
  95. }
  96. $data = fread($orgfile, $blocksize);
  97. $org_md5 = md5($data, true);
  98. if ($org_md5 == $new_md5) {
  99. $cache->set($prefix.$count, $data);
  100. } else {
  101. $needed[] = $count;
  102. }
  103. $count++;
  104. }
  105. return array(
  106. 'transferid' => $this->info['transferid'],
  107. 'needed' => $needed,
  108. 'count' => $count,
  109. );
  110. }
  111. public function file_assemble($path) {
  112. $absolutePath = \OC\Files\Filesystem::normalizePath(\OC\Files\Filesystem::getView()->getAbsolutePath($path));
  113. $data = '';
  114. // use file_put_contents as method because that best matches what this function does
  115. if (OC_FileProxy::runPreProxies('file_put_contents', $absolutePath, $data)
  116. && \OC\Files\Filesystem::isValidPath($path)) {
  117. $path = \OC\Files\Filesystem::getView()->getRelativePath($absolutePath);
  118. $exists = \OC\Files\Filesystem::file_exists($path);
  119. $run = true;
  120. if(!$exists) {
  121. OC_Hook::emit(
  122. \OC\Files\Filesystem::CLASSNAME,
  123. \OC\Files\Filesystem::signal_create,
  124. array(
  125. \OC\Files\Filesystem::signal_param_path => $path,
  126. \OC\Files\Filesystem::signal_param_run => &$run
  127. )
  128. );
  129. }
  130. OC_Hook::emit(
  131. \OC\Files\Filesystem::CLASSNAME,
  132. \OC\Files\Filesystem::signal_write,
  133. array(
  134. \OC\Files\Filesystem::signal_param_path => $path,
  135. \OC\Files\Filesystem::signal_param_run => &$run
  136. )
  137. );
  138. if(!$run) {
  139. return false;
  140. }
  141. $target = \OC\Files\Filesystem::fopen($path, 'w');
  142. if($target) {
  143. $count = $this->assemble($target);
  144. fclose($target);
  145. if(!$exists) {
  146. OC_Hook::emit(
  147. \OC\Files\Filesystem::CLASSNAME,
  148. \OC\Files\Filesystem::signal_post_create,
  149. array( \OC\Files\Filesystem::signal_param_path => $path)
  150. );
  151. }
  152. OC_Hook::emit(
  153. \OC\Files\Filesystem::CLASSNAME,
  154. \OC\Files\Filesystem::signal_post_write,
  155. array( \OC\Files\Filesystem::signal_param_path => $path)
  156. );
  157. OC_FileProxy::runPostProxies('file_put_contents', $absolutePath, $count);
  158. return $count > 0;
  159. }else{
  160. return false;
  161. }
  162. }
  163. }
  164. }