OC_FileChunking.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. class OC_FileChunking {
  8. protected $info;
  9. protected $cache;
  10. /**
  11. * TTL of chunks
  12. *
  13. * @var int
  14. */
  15. protected $ttl;
  16. public static function decodeName($name) {
  17. preg_match('/(?P<name>.*)-chunking-(?P<transferid>\d+)-(?P<chunkcount>\d+)-(?P<index>\d+)/', $name, $matches);
  18. return $matches;
  19. }
  20. /**
  21. * @param string[] $info
  22. */
  23. public function __construct($info) {
  24. $this->info = $info;
  25. $this->ttl = \OC::$server->getConfig()->getSystemValueInt('cache_chunk_gc_ttl', 86400);
  26. }
  27. public function getPrefix() {
  28. $name = $this->info['name'];
  29. $transferid = $this->info['transferid'];
  30. return $name.'-chunking-'.$transferid.'-';
  31. }
  32. protected function getCache() {
  33. if (!isset($this->cache)) {
  34. $this->cache = new \OC\Cache\File();
  35. }
  36. return $this->cache;
  37. }
  38. /**
  39. * Stores the given $data under the given $key - the number of stored bytes is returned
  40. *
  41. * @param string $index
  42. * @param resource $data
  43. * @return int
  44. */
  45. public function store($index, $data) {
  46. $cache = $this->getCache();
  47. $name = $this->getPrefix().$index;
  48. $cache->set($name, $data, $this->ttl);
  49. return $cache->size($name);
  50. }
  51. public function isComplete() {
  52. $prefix = $this->getPrefix();
  53. $cache = $this->getCache();
  54. $chunkcount = (int)$this->info['chunkcount'];
  55. for ($i = ($chunkcount - 1); $i >= 0; $i--) {
  56. if (!$cache->hasKey($prefix.$i)) {
  57. return false;
  58. }
  59. }
  60. return true;
  61. }
  62. /**
  63. * Assembles the chunks into the file specified by the path.
  64. * Chunks are deleted afterwards.
  65. *
  66. * @param resource $f target path
  67. *
  68. * @return integer assembled file size
  69. *
  70. * @throws \OC\InsufficientStorageException when file could not be fully
  71. * assembled due to lack of free space
  72. */
  73. public function assemble($f) {
  74. $cache = $this->getCache();
  75. $prefix = $this->getPrefix();
  76. $count = 0;
  77. for ($i = 0; $i < $this->info['chunkcount']; $i++) {
  78. $chunk = $cache->get($prefix.$i);
  79. // remove after reading to directly save space
  80. $cache->remove($prefix.$i);
  81. $count += fwrite($f, $chunk);
  82. // let php release the memory to work around memory exhausted error with php 5.6
  83. $chunk = null;
  84. }
  85. return $count;
  86. }
  87. /**
  88. * Returns the size of the chunks already present
  89. * @return integer size in bytes
  90. */
  91. public function getCurrentSize() {
  92. $cache = $this->getCache();
  93. $prefix = $this->getPrefix();
  94. $total = 0;
  95. for ($i = 0; $i < $this->info['chunkcount']; $i++) {
  96. $total += $cache->size($prefix.$i);
  97. }
  98. return $total;
  99. }
  100. /**
  101. * Removes all chunks which belong to this transmission
  102. */
  103. public function cleanup() {
  104. $cache = $this->getCache();
  105. $prefix = $this->getPrefix();
  106. for ($i = 0; $i < $this->info['chunkcount']; $i++) {
  107. $cache->remove($prefix.$i);
  108. }
  109. }
  110. /**
  111. * Removes one specific chunk
  112. * @param string $index
  113. */
  114. public function remove($index) {
  115. $cache = $this->getCache();
  116. $prefix = $this->getPrefix();
  117. $cache->remove($prefix.$index);
  118. }
  119. /**
  120. * Assembles the chunks into the file specified by the path.
  121. * Also triggers the relevant hooks and proxies.
  122. *
  123. * @param \OC\Files\Storage\Storage $storage storage
  124. * @param string $path target path relative to the storage
  125. * @return bool true on success or false if file could not be created
  126. *
  127. * @throws \OC\ServerNotAvailableException
  128. */
  129. public function file_assemble($storage, $path) {
  130. // use file_put_contents as method because that best matches what this function does
  131. if (\OC\Files\Filesystem::isValidPath($path)) {
  132. $target = $storage->fopen($path, 'w');
  133. if ($target) {
  134. $count = $this->assemble($target);
  135. fclose($target);
  136. return $count > 0;
  137. } else {
  138. return false;
  139. }
  140. }
  141. return false;
  142. }
  143. }