Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

AssemblyStream.php 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. <?php
  2. /**
  3. * @author Thomas Müller <thomas.mueller@tmit.eu>
  4. *
  5. * @copyright Copyright (c) 2016, ownCloud, Inc.
  6. * @license AGPL-3.0
  7. *
  8. * This code is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Affero General Public License, version 3,
  10. * as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License, version 3,
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>
  19. *
  20. */
  21. namespace OCA\DAV\Upload;
  22. use Sabre\DAV\IFile;
  23. /**
  24. * Class AssemblyStream
  25. *
  26. * The assembly stream is a virtual stream that wraps multiple chunks.
  27. * Reading from the stream transparently accessed the underlying chunks and
  28. * give a representation as if they were already merged together.
  29. *
  30. * @package OCA\DAV\Upload
  31. */
  32. class AssemblyStream implements \Icewind\Streams\File {
  33. /** @var resource */
  34. private $context;
  35. /** @var IFile[] */
  36. private $nodes;
  37. /** @var int */
  38. private $pos = 0;
  39. /** @var array */
  40. private $sortedNodes;
  41. /** @var int */
  42. private $size;
  43. /**
  44. * @param string $path
  45. * @param string $mode
  46. * @param int $options
  47. * @param string &$opened_path
  48. * @return bool
  49. */
  50. public function stream_open($path, $mode, $options, &$opened_path) {
  51. $this->loadContext('assembly');
  52. // sort the nodes
  53. $nodes = $this->nodes;
  54. // http://stackoverflow.com/a/10985500
  55. @usort($nodes, function(IFile $a, IFile $b) {
  56. return strcmp($a->getName(), $b->getName());
  57. });
  58. $this->nodes = $nodes;
  59. // build additional information
  60. $this->sortedNodes = [];
  61. $start = 0;
  62. foreach($this->nodes as $node) {
  63. $size = $node->getSize();
  64. $name = $node->getName();
  65. $this->sortedNodes[$name] = ['node' => $node, 'start' => $start, 'end' => $start + $size];
  66. $start += $size;
  67. $this->size = $start;
  68. }
  69. return true;
  70. }
  71. /**
  72. * @param string $offset
  73. * @param int $whence
  74. * @return bool
  75. */
  76. public function stream_seek($offset, $whence = SEEK_SET) {
  77. return false;
  78. }
  79. /**
  80. * @return int
  81. */
  82. public function stream_tell() {
  83. return $this->pos;
  84. }
  85. /**
  86. * @param int $count
  87. * @return string
  88. */
  89. public function stream_read($count) {
  90. list($node, $posInNode) = $this->getNodeForPosition($this->pos);
  91. if (is_null($node)) {
  92. return null;
  93. }
  94. $stream = $this->getStream($node);
  95. fseek($stream, $posInNode);
  96. $data = fread($stream, $count);
  97. $read = strlen($data);
  98. // update position
  99. $this->pos += $read;
  100. return $data;
  101. }
  102. /**
  103. * @param string $data
  104. * @return int
  105. */
  106. public function stream_write($data) {
  107. return false;
  108. }
  109. /**
  110. * @param int $option
  111. * @param int $arg1
  112. * @param int $arg2
  113. * @return bool
  114. */
  115. public function stream_set_option($option, $arg1, $arg2) {
  116. return false;
  117. }
  118. /**
  119. * @param int $size
  120. * @return bool
  121. */
  122. public function stream_truncate($size) {
  123. return false;
  124. }
  125. /**
  126. * @return array
  127. */
  128. public function stream_stat() {
  129. return [];
  130. }
  131. /**
  132. * @param int $operation
  133. * @return bool
  134. */
  135. public function stream_lock($operation) {
  136. return false;
  137. }
  138. /**
  139. * @return bool
  140. */
  141. public function stream_flush() {
  142. return false;
  143. }
  144. /**
  145. * @return bool
  146. */
  147. public function stream_eof() {
  148. return $this->pos >= $this->size;
  149. }
  150. /**
  151. * @return bool
  152. */
  153. public function stream_close() {
  154. return true;
  155. }
  156. /**
  157. * Load the source from the stream context and return the context options
  158. *
  159. * @param string $name
  160. * @return array
  161. * @throws \Exception
  162. */
  163. protected function loadContext($name) {
  164. $context = stream_context_get_options($this->context);
  165. if (isset($context[$name])) {
  166. $context = $context[$name];
  167. } else {
  168. throw new \BadMethodCallException('Invalid context, "' . $name . '" options not set');
  169. }
  170. if (isset($context['nodes']) and is_array($context['nodes'])) {
  171. $this->nodes = $context['nodes'];
  172. } else {
  173. throw new \BadMethodCallException('Invalid context, nodes not set');
  174. }
  175. return $context;
  176. }
  177. /**
  178. * @param IFile[] $nodes
  179. * @return resource
  180. *
  181. * @throws \BadMethodCallException
  182. */
  183. public static function wrap(array $nodes) {
  184. $context = stream_context_create([
  185. 'assembly' => [
  186. 'nodes' => $nodes]
  187. ]);
  188. stream_wrapper_register('assembly', '\OCA\DAV\Upload\AssemblyStream');
  189. try {
  190. $wrapped = fopen('assembly://', 'r', null, $context);
  191. } catch (\BadMethodCallException $e) {
  192. stream_wrapper_unregister('assembly');
  193. throw $e;
  194. }
  195. stream_wrapper_unregister('assembly');
  196. return $wrapped;
  197. }
  198. /**
  199. * @param $pos
  200. * @return IFile | null
  201. */
  202. private function getNodeForPosition($pos) {
  203. foreach($this->sortedNodes as $node) {
  204. if ($pos >= $node['start'] && $pos < $node['end']) {
  205. return [$node['node'], $pos - $node['start']];
  206. }
  207. }
  208. return null;
  209. }
  210. /**
  211. * @param IFile $node
  212. * @return resource
  213. */
  214. private function getStream(IFile $node) {
  215. $data = $node->get();
  216. if (is_resource($data)) {
  217. return $data;
  218. }
  219. return fopen('data://text/plain,' . $data,'r');
  220. }
  221. }