Completed
Pull Request — master (#32044)
by Thomas
19:39
created

OC_FileChunking::file_assemble()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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