5 use Laminas\Diactoros\Response\EmptyResponse
;
6 use Psr\Http\Message\ResponseInterface
;
7 use Psr\Http\Message\ServerRequestInterface
;
8 use Psr\Http\Server\RequestHandlerInterface
;
10 use wcf\system\exception\IllegalLinkException
;
11 use wcf\system\io\AtomicWriter
;
14 final class FileUploadAction
implements RequestHandlerInterface
16 public function handle(ServerRequestInterface
$request): ResponseInterface
18 // TODO: `sequenceNo` should be of type `non-negative-int`, but requires Valinor 1.7+
19 $parameters = Helper
::mapQueryParameters(
20 $request->getQueryParams(),
23 identifier: non-empty-string,
30 FROM wcf1_file_temporary
31 WHERE identifier = ?";
32 $statement = WCF
::getDB()->prepare($sql);
33 $statement->execute([$parameters['identifier']]);
34 $row = $statement->fetchSingleRow();
37 throw new IllegalLinkException();
40 // Check if this is a valid sequence no.
41 // TODO: The chunk calculation shouldn’t be based on a fixed number.
42 $chunkSize = 2_000_000
;
43 $chunks = (int)\
ceil($row['filesize'] / $chunkSize);
44 if ($parameters['sequenceNo'] >= $chunks) {
45 throw new IllegalLinkException();
48 $folderA = \
substr($row['identifier'], 0, 2);
49 $folderB = \
substr($row['identifier'], 2, 2);
52 \WCF_DIR
. '_data/private/fileUpload/%s/%s/',
56 if (!\
is_dir($tmpPath)) {
57 \
mkdir($tmpPath, recursive
: true);
63 $parameters['sequenceNo'],
66 // Write the chunk using a buffer to avoid blowing up the memory limit.
67 // See https://stackoverflow.com/a/61997147
68 $file = new AtomicWriter($tmpPath . $filename);
69 $bufferSize = 1 * 1024 * 1024;
71 $fh = \fopen
('php://input', 'rb');
73 $file->write(\fread
($fh, $bufferSize));
79 // Check if we have all chunks.
81 for ($i = 0; $i < $chunks; $i++
) {
88 if (\file_exists
($tmpPath . $filename)) {
89 $data[] = $tmpPath . $filename;
93 if (\
count($data) === $chunks) {
94 // Concatenate the files by reading only a limited buffer at a time
95 // to avoid blowing up the memory limit.
96 // See https://stackoverflow.com/a/61997147
97 $bufferSize = 1 * 1024 * 1024;
99 $newFilename = \
sprintf('%s-final.bin', $row['identifier']);
100 $file = new AtomicWriter($tmpPath . $newFilename);
101 foreach ($data as $fileChunk) {
102 $fh = \fopen
($fileChunk, 'rb');
103 while (!\feof
($fh)) {
104 $file->write(\fread
($fh, $bufferSize));
112 \
memory_get_peak_usage(true),
115 $tmpPath . $newFilename,
120 \
wcfDebug(\
memory_get_peak_usage(true));
122 // TODO: Dummy response to simulate a successful upload of a chunk.
123 return new EmptyResponse();