1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Handles file specific actions |
4
|
|
|
*/ |
5
|
|
|
|
6
|
|
|
namespace Graviton\FileBundle; |
7
|
|
|
|
8
|
|
|
use Gaufrette\File; |
9
|
|
|
use Gaufrette\FileSystem; |
10
|
|
|
use Graviton\RestBundle\Model\DocumentModel; |
11
|
|
|
use GravitonDyn\FileBundle\Document\File as FileDocument; |
12
|
|
|
use Symfony\Component\HttpFoundation\File\Exception\UploadException; |
13
|
|
|
use Symfony\Component\HttpFoundation\File\UploadedFile; |
14
|
|
|
use Symfony\Component\HttpFoundation\Request; |
15
|
|
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* @author List of contributors <https://github.com/libgraviton/graviton/graphs/contributors> |
19
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GNU Public License |
20
|
|
|
* @link http://swisscom.ch |
21
|
|
|
*/ |
22
|
|
|
class FileManager |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* @var FileSystem |
26
|
|
|
*/ |
27
|
|
|
private $fileSystem; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @var FileDocumentFactory |
31
|
|
|
*/ |
32
|
|
|
private $fileDocumentFactory; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* FileManager constructor. |
36
|
|
|
* |
37
|
|
|
* @param FileSystem $fileSystem file system abstraction layer for s3 and more |
38
|
|
|
* @param FileDocumentFactory $fileDocumentFactory Instance to be used to create action entries. |
39
|
|
|
*/ |
40
|
|
|
public function __construct(FileSystem $fileSystem, FileDocumentFactory $fileDocumentFactory) |
41
|
|
|
{ |
42
|
|
|
$this->fileSystem = $fileSystem; |
43
|
|
|
$this->fileDocumentFactory = $fileDocumentFactory; |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Indicates whether the file matching the specified key exists |
48
|
|
|
* |
49
|
|
|
* @param string $key Identifier to be found |
50
|
|
|
* |
51
|
|
|
* @return boolean TRUE if the file exists, FALSE otherwise |
52
|
|
|
*/ |
53
|
|
|
public function has($key) |
54
|
|
|
{ |
55
|
|
|
return $this->fileSystem->has($key); |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Deletes the file matching the specified key |
60
|
|
|
* |
61
|
|
|
* @param string $key Identifier to be deleted |
62
|
|
|
* |
63
|
|
|
* @throws \RuntimeException when cannot read file |
64
|
|
|
* |
65
|
|
|
* @return boolean |
66
|
|
|
*/ |
67
|
|
|
public function delete($key) |
|
|
|
|
68
|
|
|
{ |
69
|
|
|
return $this->fileSystem->delete($key); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Reads the content from the file |
74
|
|
|
* |
75
|
|
|
* @param string $key Key of the file |
76
|
|
|
* |
77
|
|
|
* @throws \Gaufrette\Exception\FileNotFound when file does not exist |
78
|
|
|
* @throws \RuntimeException when cannot read file |
79
|
|
|
* |
80
|
|
|
* @return string |
|
|
|
|
81
|
|
|
*/ |
82
|
|
|
public function read($key) |
83
|
|
|
{ |
84
|
|
|
return $this->fileSystem->read($key); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Stores uploaded files to CDN |
89
|
|
|
* |
90
|
|
|
* @param Request $request Current Http request |
91
|
|
|
* @param DocumentModel $model Model to be used to manage entity |
92
|
|
|
* @param FileDocument|null $fileData meta information about the file to be stored. |
93
|
|
|
* |
94
|
|
|
* @return array |
95
|
|
|
*/ |
96
|
|
|
public function saveFiles(Request $request, DocumentModel $model, FileDocument $fileData = null) |
97
|
|
|
{ |
98
|
|
|
$inStore = []; |
99
|
|
|
$files = $this->extractUploadedFiles($request); |
100
|
|
|
|
101
|
|
|
foreach ($files as $key => $fileInfo) { |
102
|
|
|
/** @var FileDocument $record */ |
103
|
|
|
$record = $this->createOrUpdateRecord($model, $fileData, $request->get('id')); |
104
|
|
|
$inStore[] = $record->getId(); |
105
|
|
|
|
106
|
|
|
/** @var \Gaufrette\File $file */ |
107
|
|
|
$file = $this->saveFile($record->getId(), $fileInfo['content']); |
108
|
|
|
|
109
|
|
|
$this->initOrUpdateMetadata( |
110
|
|
|
$record, |
111
|
|
|
$file->getSize(), |
112
|
|
|
$fileInfo, |
113
|
|
|
$fileData |
114
|
|
|
); |
115
|
|
|
|
116
|
|
|
$model->updateRecord($record->getId(), $record); |
117
|
|
|
|
118
|
|
|
// TODO NOTICE: ONLY UPLOAD OF ONE FILE IS CURRENTLY SUPPORTED |
119
|
|
|
break; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
return $inStore; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Save or update a file |
127
|
|
|
* |
128
|
|
|
* @param string $id ID of file |
129
|
|
|
* @param String $data content to save |
130
|
|
|
* |
131
|
|
|
* @return File |
132
|
|
|
* |
133
|
|
|
* @throws BadRequestHttpException |
134
|
|
|
*/ |
135
|
|
|
public function saveFile($id, $data) |
136
|
|
|
{ |
137
|
|
|
if (is_resource($data)) { |
138
|
|
|
throw new BadRequestHttpException('/file does not support storing resources'); |
139
|
|
|
} |
140
|
|
|
$file = new File($id, $this->fileSystem); |
141
|
|
|
$file->setContent($data); |
142
|
|
|
|
143
|
|
|
return $file; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Moves uploaded files to tmp directory |
148
|
|
|
* |
149
|
|
|
* @param Request $request Current http request |
150
|
|
|
* |
151
|
|
|
* @return array |
152
|
|
|
*/ |
153
|
|
|
private function extractUploadedFiles(Request $request) |
154
|
|
|
{ |
155
|
|
|
$uploadedFiles = []; |
156
|
|
|
|
157
|
|
|
/** @var $uploadedFile \Symfony\Component\HttpFoundation\File\UploadedFile */ |
158
|
|
|
foreach ($request->files->all() as $field => $uploadedFile) { |
159
|
|
|
if (0 === $uploadedFile->getError()) { |
160
|
|
|
$uploadedFiles[$field] = [ |
161
|
|
|
'data' => [ |
162
|
|
|
'mimetype' => $uploadedFile->getMimeType(), |
163
|
|
|
'filename' => $uploadedFile->getClientOriginalName() |
164
|
|
|
], |
165
|
|
|
'content' => file_get_contents($uploadedFile->getPathName()) |
166
|
|
|
]; |
167
|
|
|
} else { |
168
|
|
|
throw new UploadException($uploadedFile->getErrorMessage()); |
169
|
|
|
} |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
if (empty($uploadedFiles)) { |
173
|
|
|
$uploadedFiles['upload'] = [ |
174
|
|
|
'data' => [ |
175
|
|
|
'mimetype' => $request->headers->get('Content-Type'), |
176
|
|
|
'filename' => '' |
177
|
|
|
], |
178
|
|
|
'content' => $request->getContent() |
179
|
|
|
]; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
return $uploadedFiles; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Creates a new or updates an existing instance of the file document |
187
|
|
|
* |
188
|
|
|
* @param DocumentModel $model Document model |
189
|
|
|
* @param FileDocument|null $fileData File information |
190
|
|
|
* @param string $id Alternative Id to be checked |
191
|
|
|
* |
192
|
|
|
* @return FileDocument |
193
|
|
|
*/ |
194
|
|
|
private function createOrUpdateRecord(DocumentModel $model, FileDocument $fileData = null, $id = '') |
195
|
|
|
{ |
196
|
|
|
$id = empty($id) && !empty($fileData) ? $fileData->getId() : $id; |
197
|
|
|
|
198
|
|
|
if (($recordExists = empty($record = $model->find($id))) && empty($record = $fileData)) { |
199
|
|
|
$entityClass = $model->getEntityClass(); |
200
|
|
|
|
201
|
|
|
$record = new $entityClass(); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
if (!empty($id)) { |
205
|
|
|
$record->setId($id); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
return $recordExists ? $model->updateRecord($record->getId(), $record) : $model->insertRecord($record); |
|
|
|
|
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Updates or initialzes the metadata information of the current entity. |
213
|
|
|
* |
214
|
|
|
* @param FileDocument $file Document to be used |
215
|
|
|
* @param integer $fileSize Size of the uploaded file |
216
|
|
|
* @param array $fileInfo Additional info about the file |
217
|
|
|
* @param FileDocument $fileData File data to be updated |
|
|
|
|
218
|
|
|
* |
219
|
|
|
* @return void |
220
|
|
|
*/ |
221
|
|
|
private function initOrUpdateMetaData(FileDocument $file, $fileSize, array $fileInfo, FileDocument $fileData = null) |
222
|
|
|
{ |
223
|
|
|
if (empty($meta = $file->getMetadata()) && (empty($fileData) || empty($meta = $fileData->getMetadata()))) { |
224
|
|
|
$meta = $this->fileDocumentFactory->createFileMataData(); |
225
|
|
|
$meta->setId($file->getId()); |
226
|
|
|
$meta->setCreatedate(new \DateTime()); |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
$meta->setModificationdate(new \DateTime()); |
230
|
|
|
if (empty($meta->getFilename()) && !empty($fileInfo['data']['filename'])) { |
231
|
|
|
$meta->setFilename($fileInfo['data']['filename']); |
232
|
|
|
} |
233
|
|
|
if (!empty($fileInfo['data']['mimetype'])) { |
234
|
|
|
$meta->setMime($fileInfo['data']['mimetype']); |
235
|
|
|
} |
236
|
|
|
$meta->setSize($fileSize); |
237
|
|
|
$file->setMetadata($meta); |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* Extracts different information sent in the request content. |
242
|
|
|
* |
243
|
|
|
* @param Request $request Current http request |
244
|
|
|
* |
245
|
|
|
* @return array |
|
|
|
|
246
|
|
|
*/ |
247
|
|
|
public function extractDataFromRequestContent(Request $request) |
248
|
|
|
{ |
249
|
|
|
// split content |
250
|
|
|
$contentType = $request->headers->get('Content-Type'); |
251
|
|
|
list(, $boundary) = explode('; boundary=', $contentType); |
252
|
|
|
|
253
|
|
|
// fix boundary dash count |
254
|
|
|
$boundary = '--' . $boundary; |
255
|
|
|
|
256
|
|
|
$content = $request->getContent(); |
257
|
|
|
$contentBlocks = explode($boundary, $content, -1); |
258
|
|
|
$metadataInfo = ''; |
259
|
|
|
$fileInfo = ''; |
260
|
|
|
|
261
|
|
|
// determine content blocks usage |
262
|
|
|
foreach ($contentBlocks as $contentBlock) { |
263
|
|
|
if (empty($contentBlock)) { |
264
|
|
|
continue; |
265
|
|
|
} |
266
|
|
|
if (40 === strpos($contentBlock, 'upload')) { |
267
|
|
|
$fileInfo = $contentBlock; |
268
|
|
|
continue; |
269
|
|
|
} |
270
|
|
|
if (40 === strpos($contentBlock, 'metadata')) { |
271
|
|
|
$metadataInfo = $contentBlock; |
272
|
|
|
continue; |
273
|
|
|
} |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
$attributes = array_merge( |
277
|
|
|
$request->attributes->all(), |
278
|
|
|
$this->extractMetaDataFromContent($metadataInfo) |
279
|
|
|
); |
280
|
|
|
$files = $this->extractFileFromContent($fileInfo); |
281
|
|
|
|
282
|
|
|
return ['files' => $files, 'attributes' => $attributes]; |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* Extracts meta information from request content. |
287
|
|
|
* |
288
|
|
|
* @param string $metadataInfoString Information about metadata information |
289
|
|
|
* |
290
|
|
|
* @return array |
|
|
|
|
291
|
|
|
*/ |
292
|
|
|
private function extractMetaDataFromContent($metadataInfoString) |
293
|
|
|
{ |
294
|
|
|
if (empty($metadataInfoString)) { |
295
|
|
|
return ['metadata' => '{}']; |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
$metadataInfo = explode("\r\n", ltrim($metadataInfoString)); |
299
|
|
|
return ['metadata' => $metadataInfo[2]]; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Extracts file data from request content |
304
|
|
|
* |
305
|
|
|
* @param string $fileInfoString Information about uploaded files. |
306
|
|
|
* |
307
|
|
|
* @return array |
|
|
|
|
308
|
|
|
*/ |
309
|
|
|
private function extractFileFromContent($fileInfoString) |
310
|
|
|
{ |
311
|
|
|
if (empty($fileInfoString)) { |
312
|
|
|
return null; |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
$fileInfo = explode("\r\n\r\n", ltrim($fileInfoString), 2); |
316
|
|
|
|
317
|
|
|
// write content to file ("upload_tmp_dir" || sys_get_temp_dir() ) |
318
|
|
|
preg_match('@name=\"([^"]*)\";\sfilename=\"([^"]*)\"\s*Content-Type:\s([^"]*)@', $fileInfo[0], $matches); |
319
|
|
|
$originalName = $matches[2]; |
320
|
|
|
$dir = ini_get('upload_tmp_dir'); |
321
|
|
|
$dir = (empty($dir)) ? sys_get_temp_dir() : $dir; |
322
|
|
|
$file = $dir . '/' . $originalName; |
323
|
|
|
$fileContent = substr($fileInfo[1], 0, -2); |
324
|
|
|
|
325
|
|
|
// create file |
326
|
|
|
touch($file); |
327
|
|
|
$size = file_put_contents($file, $fileContent, LOCK_EX); |
328
|
|
|
|
329
|
|
|
$files = [ |
330
|
|
|
$matches[1] => new UploadedFile( |
331
|
|
|
$file, |
332
|
|
|
$originalName, |
333
|
|
|
$matches[3], |
334
|
|
|
$size |
335
|
|
|
) |
336
|
|
|
]; |
337
|
|
|
|
338
|
|
|
return $files; |
339
|
|
|
} |
340
|
|
|
} |
341
|
|
|
|
This check examines a number of code elements and verifies that they conform to the given naming conventions.
You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.