Skip to content
This repository was archived by the owner on May 30, 2023. It is now read-only.

Commit 7a920f2

Browse files
author
Robert Kummer
committed
modifying an existing proxy file added
1 parent a355987 commit 7a920f2

File tree

8 files changed

+513
-0
lines changed

8 files changed

+513
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace App\Exceptions;
4+
5+
class LocalProxyFileCanNotByDeleted extends \Exception
6+
{
7+
public static function throwException(): self
8+
{
9+
return new static('Local proxy file can not be deleted.');
10+
}
11+
}

app/Http/Controllers/Api/FilesController.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use App\Http\Requests\Api\CreateFileRequest;
66
use App\Jobs\CreateLocalFile;
77
use App\Jobs\CreateRemoteFile;
8+
use App\Jobs\UpdateLocalFile;
9+
use App\Jobs\UpdateRemoteFile;
810
use App\ProxyFile;
911
use App\Transformers\FileAliasTransformer;
1012
use App\Transformers\ProxyFileTransformer;
@@ -94,4 +96,31 @@ public function showAliases(string $file)
9496

9597
return $this->respondCollection($proxyFile->aliases, new FileAliasTransformer(), 'aliases');
9698
}
99+
100+
public function update(CreateFileRequest $request, string $file)
101+
{
102+
$proxyFile = ProxyFile::byReference($file);
103+
104+
if ($request->isAttachment()) {
105+
$content = $request->source();
106+
$filename = $request->filename();
107+
108+
$mimetypes = new MimeTypes();
109+
$parts = explode('.', $filename);
110+
$extension = last($parts);
111+
$mimetype = $mimetypes->getMimeType($extension);
112+
113+
$tempFilename = tempnam(sys_get_temp_dir(), 'proxyfile_');
114+
file_put_contents($tempFilename, $content);
115+
116+
$job = new UpdateLocalFile($proxyFile, new UploadedFile($tempFilename, $filename, $mimetype));
117+
} else {
118+
$url = $request->source();
119+
$job = new UpdateRemoteFile($proxyFile, $url);
120+
}
121+
122+
$this->dispatch($job);
123+
124+
return $this->respondCreated($proxyFile, new ProxyFileTransformer(), 'files');
125+
}
97126
}

app/Jobs/UpdateLocalFile.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
namespace App\Jobs;
4+
5+
use App\Exceptions\LocalProxyFileCanNotByDeleted;
6+
use App\LocalFile;
7+
use App\ProxyFile;
8+
use App\RemoteFile;
9+
use Illuminate\Contracts\Filesystem\Filesystem;
10+
use Illuminate\Foundation\Bus\Dispatchable;
11+
use Illuminate\Http\UploadedFile;
12+
13+
class UpdateLocalFile
14+
{
15+
use Dispatchable;
16+
17+
/**
18+
* @var ProxyFile
19+
*/
20+
private $proxyFile;
21+
22+
/**
23+
* @var UploadedFile
24+
*/
25+
private $file;
26+
27+
/**
28+
* Create a new job instance.
29+
*
30+
* @param ProxyFile $proxyFile
31+
* @param UploadedFile $file
32+
*/
33+
public function __construct(ProxyFile $proxyFile, UploadedFile $file)
34+
{
35+
$this->proxyFile = $proxyFile;
36+
$this->file = $file;
37+
}
38+
39+
/**
40+
* Execute the job.
41+
*
42+
* @return void
43+
* @throws \App\Exceptions\LocalProxyFileCanNotByDeleted
44+
*/
45+
public function handle()
46+
{
47+
/** @var Filesystem|\Illuminate\Filesystem\FilesystemAdapter $filesystem */
48+
$filesystem = app(Filesystem::class);
49+
50+
// remove previous file content
51+
52+
/** @var LocalFile|RemoteFile $currentFile */
53+
$currentFile = $this->proxyFile->file;
54+
if (!$filesystem->delete($currentFile->getLocalStoragePath())) {
55+
throw LocalProxyFileCanNotByDeleted::throwException();
56+
}
57+
$currentFile->forceDelete();
58+
59+
// proceed new file content
60+
61+
$fileHandle = $this->file->openFile();
62+
$content = $fileHandle->fread($this->file->getSize());
63+
64+
$this->proxyFile->update([
65+
'type' => 'local',
66+
'filename' => $this->file->getClientOriginalName(),
67+
'mimetype' => $this->file->getClientMimeType(),
68+
'size' => $this->file->getSize(),
69+
'checksum' => sha1($content),
70+
]);
71+
72+
/** @var \App\LocalFile $localFile */
73+
$localFile = $this->proxyFile->localFile()->create([
74+
'path' => uniqid('', true),
75+
]);
76+
77+
if (!$filesystem->exists('local')) {
78+
$filesystem->makeDirectory('local');
79+
}
80+
81+
$path = $localFile->getLocalStoragePath();
82+
if ($filesystem->put($path, $content)) {
83+
$localFile->path = $path;
84+
$localFile->save();
85+
}
86+
}
87+
}

app/Jobs/UpdateRemoteFile.php

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?php
2+
3+
namespace App\Jobs;
4+
5+
use App\Exceptions\LocalProxyFileCanNotByDeleted;
6+
use App\Exceptions\RemoteFileNotAccessibleException;
7+
use App\LocalFile;
8+
use App\ProxyFile;
9+
use App\RemoteFile;
10+
use GuzzleHttp\Client;
11+
use Illuminate\Bus\Queueable;
12+
use Illuminate\Contracts\Filesystem\Filesystem;
13+
use Illuminate\Contracts\Queue\ShouldQueue;
14+
use Illuminate\Foundation\Bus\Dispatchable;
15+
use Illuminate\Queue\InteractsWithQueue;
16+
use Illuminate\Queue\SerializesModels;
17+
use Illuminate\Support\Str;
18+
use Mimey\MimeTypes;
19+
20+
class UpdateRemoteFile implements ShouldQueue
21+
{
22+
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
23+
/**
24+
* @var ProxyFile
25+
*/
26+
private $proxyFile;
27+
/**
28+
* @var string
29+
*/
30+
private $url;
31+
/**
32+
* @var array
33+
*/
34+
private $options;
35+
36+
/**
37+
* Create a new job instance.
38+
*
39+
* @param ProxyFile $proxyFile
40+
* @param string $url
41+
* @param array $options
42+
*/
43+
public function __construct(ProxyFile $proxyFile, string $url, array $options = [])
44+
{
45+
$this->proxyFile = $proxyFile;
46+
$this->url = $url;
47+
$this->options = $options;
48+
}
49+
50+
/**
51+
* Execute the job.
52+
* @throws \App\Exceptions\RemoteFileNotAccessibleException
53+
* @throws \Exception
54+
* @throws \App\Exceptions\LocalProxyFileCanNotByDeleted
55+
*/
56+
public function handle()
57+
{
58+
$client = new Client();
59+
$method = array_get($this->options, 'method', 'GET');
60+
$response = $client->request($method, $this->url, $this->options);
61+
if ($response->getStatusCode() > 299) {
62+
throw new RemoteFileNotAccessibleException($this->url);
63+
}
64+
65+
$path = null;
66+
67+
// remove previous file content
68+
69+
/** @var LocalFile|RemoteFile $currentFile */
70+
$currentFile = $this->proxyFile->file;
71+
if (!$this->getFilesystem()->delete($currentFile->getLocalStoragePath())) {
72+
throw LocalProxyFileCanNotByDeleted::throwException();
73+
}
74+
$currentFile->forceDelete();
75+
76+
// proceed new file content
77+
78+
try {
79+
\DB::beginTransaction();
80+
81+
$content = (string)$response->getBody();
82+
83+
$filename = basename($this->url);
84+
$parts = explode('.', $filename);
85+
$extension = last($parts);
86+
87+
$mimetype = new MimeTypes();
88+
89+
$this->proxyFile->update([
90+
'type' => 'remote',
91+
'filename' => $filename,
92+
'mimetype' => $mimetype->getMimeType($extension),
93+
'size' => Str::length($content),
94+
'checksum' => sha1($content),
95+
]);
96+
97+
/** @var \App\RemoteFile $remoteFile */
98+
$remoteFile = $this->proxyFile->remoteFile()->create([
99+
'url' => $this->url,
100+
'options' => $this->options,
101+
]);
102+
103+
$this->cacheRemoteFileLocally($remoteFile, $content);
104+
105+
\DB::commit();
106+
} catch (\Exception $exception) {
107+
\DB::rollBack();
108+
if ($path !== null) {
109+
@unlink(storage_path('app' . DIRECTORY_SEPARATOR . $path));
110+
}
111+
throw $exception;
112+
}
113+
}
114+
115+
/**
116+
* cache remote file locally.
117+
*
118+
* @param \App\RemoteFile $remoteFile
119+
* @param string $content
120+
*/
121+
private function cacheRemoteFileLocally(RemoteFile $remoteFile, string $content)
122+
{
123+
if (config('fileproxy.cache_remote_files', false) === false) {
124+
return;
125+
}
126+
127+
$path = $remoteFile->getLocalStoragePath();
128+
if ($this->getFilesystem()->put($path, $content)) {
129+
$remoteFile->path = $path;
130+
$remoteFile->save();
131+
}
132+
}
133+
134+
/**
135+
* sets up filesystem
136+
*
137+
* @return Filesystem|\Illuminate\Filesystem\FilesystemAdapter
138+
*/
139+
private function getFilesystem()
140+
{
141+
/** @var Filesystem|\Illuminate\Filesystem\FilesystemAdapter $filesystem */
142+
$filesystem = app(Filesystem::class);
143+
if (!$filesystem->exists('remote')) {
144+
$filesystem->makeDirectory('remote');
145+
}
146+
return $filesystem;
147+
}
148+
}

routes/api.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
'only' => [
2222
'store',
2323
'show',
24+
'update',
2425
],
2526
]);
2627

tests/Feature/Api/FilesResourceTest.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,4 +226,61 @@ public function it_can_fetch_a_files_resource_by_reference_with_aliases_included
226226
]);
227227
}
228228

229+
/** @test */
230+
public function it_can_update_a_proxy_file_via_api()
231+
{
232+
// ARRANGE
233+
config(['filesystems.default' => 'local']);
234+
$response = $this->postJson('/api/files', $this->createRequestModel('files', [
235+
'type' => 'attachment',
236+
'source' => base64_encode('test'),
237+
'filename' => 'test.txt',
238+
]));
239+
$response->assertStatus(201);
240+
241+
/** @var ProxyFile $proxyFile */
242+
$proxyFile = ProxyFile::first();
243+
244+
// ACT
245+
$response = $this->putJson('/api/files/' . $proxyFile->reference, $this->createRequestModel('files', [
246+
'type' => 'attachment',
247+
'source' => base64_encode('test2'),
248+
'filename' => 'test2.txt',
249+
]));
250+
251+
// ASSERT
252+
$response->assertStatus(201)
253+
->assertExactJson([
254+
'data' => [
255+
'type' => 'files',
256+
'id' => $proxyFile->reference,
257+
'attributes' => [
258+
'filename' => 'test2.txt',
259+
'size' => 5,
260+
'checksum' => '109f4b3c50d7b0df729d299bc6f8e9ef9066971f',
261+
'mimetype' => 'text/plain',
262+
'hits' => 0,
263+
],
264+
'links' => [
265+
'self' => 'http://localhost/api/files/' . $proxyFile->reference,
266+
],
267+
]
268+
]);
269+
270+
$this->assertDatabaseHas('proxy_files', [
271+
'id' => 1,
272+
'filename' => 'test2.txt',
273+
]);
274+
$this->assertDatabaseHas('local_files', [
275+
'proxy_file_id' => 1,
276+
]);
277+
278+
// sha1(2) => da4b9237bacccdf19c0760cab7aec4a8359010b0
279+
$this->assertFileNotExists(storage_path('app/local/35/6a/356a192b7913b04c54574d18c28d46e6395428ab'));
280+
$this->assertFileExists(storage_path('app/local/da/4b/da4b9237bacccdf19c0760cab7aec4a8359010b0'));
281+
unlink(storage_path('app/local/da/4b/da4b9237bacccdf19c0760cab7aec4a8359010b0'));
282+
rmdir(storage_path('app/local/da/4b'));
283+
rmdir(storage_path('app/local/da'));
284+
}
285+
229286
}

0 commit comments

Comments
 (0)