1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Alpixel\Bundle\MediaBundle\Services; |
4
|
|
|
|
5
|
|
|
use Alpixel\Bundle\MediaBundle\Entity\Media; |
6
|
|
|
use Alpixel\Bundle\MediaBundle\Exception\InvalidMimeTypeException; |
7
|
|
|
use Cocur\Slugify\Slugify; |
8
|
|
|
use Doctrine\ORM\EntityManager; |
9
|
|
|
use Symfony\Component\DependencyInjection\ContainerAwareTrait; |
10
|
|
|
use Symfony\Component\Filesystem\Exception\IOException; |
11
|
|
|
use Symfony\Component\Filesystem\Filesystem; |
12
|
|
|
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; |
13
|
|
|
use Symfony\Component\HttpFoundation\File\File; |
14
|
|
|
use Symfony\Component\HttpFoundation\File\UploadedFile; |
15
|
|
|
|
16
|
|
|
class MediaManager |
17
|
|
|
{ |
18
|
|
|
protected $entityManager; |
19
|
|
|
protected $uploadDir; |
20
|
|
|
protected $allowedMimetypes; |
21
|
|
|
|
22
|
|
|
use ContainerAwareTrait; |
23
|
|
|
|
24
|
|
|
const SIZE_OF_KIBIOCTET = 1024; |
25
|
|
|
const OCTET_IN_KO = 1; |
26
|
|
|
const OCTET_IN_MO = 2; |
27
|
|
|
const OCTET_IN_GO = 3; |
28
|
|
|
const OCTET_IN_TO = 4; |
29
|
|
|
const OCTET_IN_PO = 5; |
30
|
|
|
|
31
|
|
|
public function __construct(EntityManager $entityManager, $uploadDir, $allowedMimetypes) |
|
|
|
|
32
|
|
|
{ |
33
|
|
|
$this->entityManager = $entityManager; |
34
|
|
|
$this->uploadDir = $uploadDir; |
35
|
|
|
$this->allowedMimetypes = $allowedMimetypes; |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* $current_uri String actual uri of the file |
40
|
|
|
* $dest_folder String future uri of the file starting from web/upload folder |
41
|
|
|
* $lifetime DateTime lifetime of the file. If time goes over this limit, the file will be deleted. |
42
|
|
|
**/ |
43
|
|
|
public function upload(UploadedFile $file, $dest_folder = '', \DateTime $lifetime = null) |
44
|
|
|
{ |
45
|
|
|
//preparing dir name |
46
|
|
|
$dest_folder = date('Ymd').'/'.date('G').'/'.$dest_folder; |
47
|
|
|
|
48
|
|
|
//checking mimetypes |
49
|
|
|
$mimeTypePassed = false; |
50
|
|
|
foreach ($this->allowedMimetypes as $mimeType) { |
51
|
|
|
if (preg_match('@'.$mimeType.'@', $file->getMimeType())) { |
52
|
|
|
$mimeTypePassed = true; |
53
|
|
|
} |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
if (!$mimeTypePassed) { |
57
|
|
|
throw new InvalidMimeTypeException('Only following filetypes are allowed : '.implode(', ', $this->allowedMimetypes)); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
$fs = new Filesystem(); |
61
|
|
|
if (!$fs->exists($this->uploadDir.$dest_folder)) { |
62
|
|
|
$fs->mkdir($this->uploadDir.$dest_folder); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
$em = $this->entityManager; |
66
|
|
|
$media = new Media(); |
67
|
|
|
$media->setMime($file->getMimeType()); |
68
|
|
|
|
69
|
|
|
// Sanitizing the filename |
70
|
|
|
$slugify = new Slugify(); |
71
|
|
|
$filename = $slugify->slugify($file->getClientOriginalName()); |
72
|
|
|
|
73
|
|
|
// A media can have a lifetime and will be deleted with the cleanup function |
74
|
|
|
if (!empty($lifetime)) { |
75
|
|
|
$media->setLifetime($lifetime); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
// Checking for a media with the same name |
79
|
|
|
$mediaExists = $this->entityManager->getRepository('AlpixelMediaBundle:Media')->findOneByUri($dest_folder.$filename); |
80
|
|
|
if (count($mediaExists) === 0) { |
81
|
|
|
$mediaExists = $fs->exists($this->uploadDir.$dest_folder.$filename); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
// If there's one, we try to generate a new name |
85
|
|
|
$extension = $file->getExtension(); |
86
|
|
|
if (empty($extension)) { |
87
|
|
|
$extension = $file->guessExtension(); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
if (count($mediaExists) > 0) { |
91
|
|
|
$filename = basename($filename, '.'.$extension); |
92
|
|
|
|
93
|
|
|
$i = 1; |
94
|
|
|
do { |
95
|
|
|
$media->setName($filename.'-'.$i++.'.'.$extension); |
96
|
|
|
$media->setUri($dest_folder.$media->getName()); |
97
|
|
|
$mediaExists = $this->entityManager->getRepository('AlpixelMediaBundle:Media')->findOneByUri($media->getUri()); |
98
|
|
|
} while (count($mediaExists) > 0); |
99
|
|
|
} else { |
100
|
|
|
$media->setName($filename.'.'.$extension); |
101
|
|
|
$media->setUri($dest_folder.$media->getName()); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
$file->move($this->uploadDir.$dest_folder, $media->getName()); |
105
|
|
|
|
106
|
|
|
chmod($this->uploadDir.$dest_folder.$media->getName(), 0664); |
107
|
|
|
|
108
|
|
|
// Getting the salt defined in parameters.yml |
109
|
|
|
$secret = $this->container->getParameter('secret'); |
110
|
|
|
$media->setSecretKey(hash('sha256', $secret.$media->getName().$media->getUri())); |
111
|
|
|
|
112
|
|
|
$em->persist($media); |
113
|
|
|
$em->flush(); |
114
|
|
|
|
115
|
|
|
return $media; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
public function cleanup() |
119
|
|
|
{ |
120
|
|
|
$medias = $this->entityManager->getRepository('AlpixelMediaBundle:Media')->findExpiredMedias(); |
121
|
|
|
foreach ($medias as $media) { |
122
|
|
|
$this->delete($media); |
123
|
|
|
} |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
public function delete(Media $media) |
127
|
|
|
{ |
128
|
|
|
$em = $this->entityManager; |
129
|
|
|
$fs = new Filesystem(); |
130
|
|
|
|
131
|
|
|
$file_path = $this->uploadDir.$media->getUri(); |
132
|
|
|
|
133
|
|
|
try { |
134
|
|
|
$file = new File($file_path); |
135
|
|
|
if ($file->isFile() && $file->isWritable()) { |
136
|
|
|
$fs->remove($file_path); |
137
|
|
|
} |
138
|
|
|
} catch (FileNotFoundException $e) { |
|
|
|
|
139
|
|
|
} catch (IOException $e) { |
|
|
|
|
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
$em->remove($media); |
143
|
|
|
$em->flush(); |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
public function getUploadDir($filter = null) |
147
|
|
|
{ |
148
|
|
|
if (!empty($filter)) { |
149
|
|
|
return $this->uploadDir.'filters/'.$filter.'/'; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
return $this->uploadDir; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
public function getWebPath(Media $media) |
156
|
|
|
{ |
157
|
|
|
$request = $this->container->get('request'); |
158
|
|
|
$dir = $request->getSchemeAndHttpHost().$request->getBaseUrl().'/'; |
159
|
|
|
|
160
|
|
|
return $dir.$media->getUri(); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
public function getAbsolutePath(Media $media, $filter = null) |
164
|
|
|
{ |
165
|
|
|
$imgSrc = $this->uploadDir; |
166
|
|
|
if (!empty($filter)) { |
167
|
|
|
return $imgSrc.'filters/'.$filter.'/'.$media->getUri(); |
168
|
|
|
} else { |
169
|
|
|
return $imgSrc.$media->getUri(); |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
public function generateUrl(Media $media, $options) |
174
|
|
|
{ |
175
|
|
|
$defaultOptions = [ |
176
|
|
|
'public' => true, |
177
|
|
|
'action' => 'show', |
178
|
|
|
'filter' => null, |
179
|
|
|
'absolute' => false, |
180
|
|
|
]; |
181
|
|
|
|
182
|
|
|
$options = array_merge($defaultOptions, $options); |
183
|
|
|
$params = []; |
184
|
|
|
|
185
|
|
|
$routeName = 'media_'; |
186
|
|
|
if ($options['action'] === 'download') { |
187
|
|
|
$routeName .= 'download_'; |
188
|
|
|
} else { |
189
|
|
|
$routeName .= 'show_'; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
if ($options['public'] === true) { |
193
|
|
|
$routeName .= 'public'; |
194
|
|
|
$params['id'] = $media->getId(); |
195
|
|
|
$params['name'] = $media->getName(); |
196
|
|
|
} else { |
197
|
|
|
$routeName .= 'private'; |
198
|
|
|
$params['secretKey'] = $media->getSecretKey(); |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
if ($options['filter'] !== null) { |
202
|
|
|
if ($options['public'] === true) { |
203
|
|
|
$routeName .= '_filters'; |
204
|
|
|
} |
205
|
|
|
$params['filter'] = $options['filter']; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
$container = $this->container; |
209
|
|
|
|
210
|
|
|
$router = $container->get('router'); |
211
|
|
|
|
212
|
|
|
return $router->generate($routeName, $params, $options['absolute']); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
public function findFromSecret($secret) |
216
|
|
|
{ |
217
|
|
|
return $this->entityManager->getRepository('AlpixelMediaBundle:Media')->findOneBySecretKey($secret); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
public function setAllowedMimeTypes(array $type) |
221
|
|
|
{ |
222
|
|
|
if ($type !== null) { |
223
|
|
|
$this->allowedMimetypes = $type; |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
return $this; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
public function getAllowedMimeTypes() |
230
|
|
|
{ |
231
|
|
|
return $this->allowedMimetypes; |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
public function convertOctetIn($size, $convert) |
235
|
|
|
{ |
236
|
|
|
if ($convert > 0) { |
237
|
|
|
$size = ($size / self::SIZE_OF_KIBIOCTET) * 1; |
238
|
|
|
|
239
|
|
|
return $this->convertOctetIn($size, $convert - 1); |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
return $size; |
243
|
|
|
} |
244
|
|
|
} |
245
|
|
|
|
The
EntityManager
might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:If that code throws an exception and the
EntityManager
is closed. Any other code which depends on the same instance of theEntityManager
during this request will fail.On the other hand, if you instead inject the
ManagerRegistry
, thegetManager()
method guarantees that you will always get a usable manager instance.