You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

186 lines
4.8 KiB

3 years ago
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Bart Visscher <bartv@thisnet.nl>
  6. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  7. * @author Felix Moeller <mail@felixmoeller.de>
  8. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  9. * @author Morris Jobke <hey@morrisjobke.de>
  10. * @author Robin Appelman <robin@icewind.nl>
  11. * @author Roeland Jago Douma <roeland@famdouma.nl>
  12. * @author Thomas Müller <thomas.mueller@tmit.eu>
  13. * @author Thomas Tanghus <thomas@tanghus.net>
  14. * @author Vincent Petry <pvince81@owncloud.com>
  15. *
  16. * @license AGPL-3.0
  17. *
  18. * This code is free software: you can redistribute it and/or modify
  19. * it under the terms of the GNU Affero General Public License, version 3,
  20. * as published by the Free Software Foundation.
  21. *
  22. * This program is distributed in the hope that it will be useful,
  23. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  24. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  25. * GNU Affero General Public License for more details.
  26. *
  27. * You should have received a copy of the GNU Affero General Public License, version 3,
  28. * along with this program. If not, see <http://www.gnu.org/licenses/>
  29. *
  30. */
  31. class OC_FileChunking {
  32. protected $info;
  33. protected $cache;
  34. /**
  35. * TTL of chunks
  36. *
  37. * @var int
  38. */
  39. protected $ttl;
  40. public static function decodeName($name) {
  41. preg_match('/(?P<name>.*)-chunking-(?P<transferid>\d+)-(?P<chunkcount>\d+)-(?P<index>\d+)/', $name, $matches);
  42. return $matches;
  43. }
  44. /**
  45. * @param string[] $info
  46. */
  47. public function __construct($info) {
  48. $this->info = $info;
  49. $this->ttl = \OC::$server->getConfig()->getSystemValue('cache_chunk_gc_ttl', 86400);
  50. }
  51. public function getPrefix() {
  52. $name = $this->info['name'];
  53. $transferid = $this->info['transferid'];
  54. return $name.'-chunking-'.$transferid.'-';
  55. }
  56. protected function getCache() {
  57. if (!isset($this->cache)) {
  58. $this->cache = new \OC\Cache\File();
  59. }
  60. return $this->cache;
  61. }
  62. /**
  63. * Stores the given $data under the given $key - the number of stored bytes is returned
  64. *
  65. * @param string $index
  66. * @param resource $data
  67. * @return int
  68. */
  69. public function store($index, $data) {
  70. $cache = $this->getCache();
  71. $name = $this->getPrefix().$index;
  72. $cache->set($name, $data, $this->ttl);
  73. return $cache->size($name);
  74. }
  75. public function isComplete() {
  76. $prefix = $this->getPrefix();
  77. $cache = $this->getCache();
  78. $chunkcount = (int)$this->info['chunkcount'];
  79. for ($i=($chunkcount-1); $i >= 0; $i--) {
  80. if (!$cache->hasKey($prefix.$i)) {
  81. return false;
  82. }
  83. }
  84. return true;
  85. }
  86. /**
  87. * Assembles the chunks into the file specified by the path.
  88. * Chunks are deleted afterwards.
  89. *
  90. * @param resource $f target path
  91. *
  92. * @return integer assembled file size
  93. *
  94. * @throws \OC\InsufficientStorageException when file could not be fully
  95. * assembled due to lack of free space
  96. */
  97. public function assemble($f) {
  98. $cache = $this->getCache();
  99. $prefix = $this->getPrefix();
  100. $count = 0;
  101. for ($i = 0; $i < $this->info['chunkcount']; $i++) {
  102. $chunk = $cache->get($prefix.$i);
  103. // remove after reading to directly save space
  104. $cache->remove($prefix.$i);
  105. $count += fwrite($f, $chunk);
  106. // let php release the memory to work around memory exhausted error with php 5.6
  107. $chunk = null;
  108. }
  109. return $count;
  110. }
  111. /**
  112. * Returns the size of the chunks already present
  113. * @return integer size in bytes
  114. */
  115. public function getCurrentSize() {
  116. $cache = $this->getCache();
  117. $prefix = $this->getPrefix();
  118. $total = 0;
  119. for ($i = 0; $i < $this->info['chunkcount']; $i++) {
  120. $total += $cache->size($prefix.$i);
  121. }
  122. return $total;
  123. }
  124. /**
  125. * Removes all chunks which belong to this transmission
  126. */
  127. public function cleanup() {
  128. $cache = $this->getCache();
  129. $prefix = $this->getPrefix();
  130. for ($i=0; $i < $this->info['chunkcount']; $i++) {
  131. $cache->remove($prefix.$i);
  132. }
  133. }
  134. /**
  135. * Removes one specific chunk
  136. * @param string $index
  137. */
  138. public function remove($index) {
  139. $cache = $this->getCache();
  140. $prefix = $this->getPrefix();
  141. $cache->remove($prefix.$index);
  142. }
  143. /**
  144. * Assembles the chunks into the file specified by the path.
  145. * Also triggers the relevant hooks and proxies.
  146. *
  147. * @param \OC\Files\Storage\Storage $storage storage
  148. * @param string $path target path relative to the storage
  149. * @return bool true on success or false if file could not be created
  150. *
  151. * @throws \OC\ServerNotAvailableException
  152. */
  153. public function file_assemble($storage, $path) {
  154. // use file_put_contents as method because that best matches what this function does
  155. if (\OC\Files\Filesystem::isValidPath($path)) {
  156. $target = $storage->fopen($path, 'w');
  157. if ($target) {
  158. $count = $this->assemble($target);
  159. fclose($target);
  160. return $count > 0;
  161. } else {
  162. return false;
  163. }
  164. }
  165. return false;
  166. }
  167. }