From 8ab28888507cc8b226d457b56fb5fdfe638786d7 Mon Sep 17 00:00:00 2001 From: aina-esokia Date: Mon, 18 Mar 2019 16:16:14 +0400 Subject: [PATCH] port to 4.1 upload by url --- .../Phrasea/Controller/Api/V1Controller.php | 66 ++++++++++++------ .../Controller/Prod/UploadController.php | 69 +++++++++++++++++-- .../ControllerProvider/Prod/Upload.php | 3 + templates/web/prod/upload/upload.html.twig | 5 +- 4 files changed, 116 insertions(+), 27 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php index ff8764b0d9..1eb2810372 100644 --- a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php +++ b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php @@ -88,9 +88,10 @@ use Alchemy\Phrasea\Status\StatusStructure; use Alchemy\Phrasea\TaskManager\LiveInformation; use Alchemy\Phrasea\Utilities\NullableDateTime; use Doctrine\ORM\EntityManager; -use JMS\TranslationBundle\Annotation\Ignore; +use Guzzle\Http\Client as Guzzle; use League\Fractal\Resource\Item; use media_subdef; +use Neutron\TemporaryFilesystem\TemporaryFilesystemInterface; use Symfony\Component\Form\Form; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\Request; @@ -905,19 +906,6 @@ class V1Controller extends Controller public function addRecordAction(Request $request) { - if (count($request->files->get('file')) == 0) { - return $this->getBadRequestAction($request, 'Missing file parameter'); - } - - $file = $request->files->get('file'); - if (!$file instanceof UploadedFile) { - return $this->getBadRequestAction($request, 'You can upload one file at time'); - } - - if (!$file->isValid()) { - return $this->getBadRequestAction($request, 'Data corrupted, please try again'); - } - if (!$request->get('base_id')) { return $this->getBadRequestAction($request, 'Missing base_id parameter'); } @@ -930,16 +918,54 @@ class V1Controller extends Controller ))->createResponse(); } - // Add file extension - $uploadedFilename = $file->getRealPath(); + if (count($request->files->get('file')) == 0) { + if(count($request->get('url')) == 0) { + return $this->getBadRequestAction($request, 'Missing file parameter'); + } + else { + // upload via url + $url = $request->get('url'); + $pi = pathinfo($url); // filename, extension - $renamedFilename = $file->getRealPath() . '.' . pathinfo($file->getClientOriginalName(), PATHINFO_EXTENSION); + /** @var TemporaryFilesystemInterface $tmpFs */ + $tmpFs = $this->app['temporary-filesystem']; + $tempfile = $tmpFs->createTemporaryFile('download_', null, $pi['extension']); - $this->getFilesystem()->rename($uploadedFilename, $renamedFilename); + try { + $guzzle = new Guzzle($url); + $res = $guzzle->get("", [], ['save_to' => $tempfile])->send(); + } + catch (\Exception $e) { + return $this->getBadRequestAction($request, sprintf('Error "%s" downloading "%s"', $e->getMessage(), $url)); + } - $media = $this->app->getMediaFromUri($renamedFilename); + if($res->getStatusCode() !== 200) { + return $this->getBadRequestAction($request, sprintf('Error %s downloading "%s"', $res->getStatusCode(), $url)); + } - $Package = new File($this->app, $media, $collection, $file->getClientOriginalName()); + $originalName = $pi['filename'] . '.' . $pi['extension']; + $newPathname = $tempfile; + } + } + else { + // upload via file + $file = $request->files->get('file'); + if (!$file instanceof UploadedFile) { + return $this->getBadRequestAction($request, 'You can upload one file at time'); + } + if (!$file->isValid()) { + return $this->getBadRequestAction($request, 'Data corrupted, please try again'); + } + $originalName = $file->getClientOriginalName(); + $newPathname = $file->getPathname() . '.' . $file->getClientOriginalExtension(); + if (false === rename($file->getPathname(), $newPathname)) { + return Result::createError($request, 403, 'Error while renaming file')->createResponse(); + } + } + + $media = $this->app->getMediaFromUri($newPathname); + + $Package = new File($this->app, $media, $collection, $originalName); if ($request->get('status')) { $Package->addAttribute(new Status($this->app, $request->get('status'))); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/UploadController.php b/lib/Alchemy/Phrasea/Controller/Prod/UploadController.php index 47e6716ac4..89a0d21910 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/UploadController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/UploadController.php @@ -26,6 +26,7 @@ use Alchemy\Phrasea\Model\Entities\LazaretFile; use Alchemy\Phrasea\Model\Entities\LazaretSession; use DataURI\Exception\Exception as DataUriException; use DataURI\Parser; +use Guzzle\Http\Client as Guzzle; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -76,6 +77,30 @@ class UploadController extends Controller ]); } + public function getHead(Request $request) + { + $response = [ + 'content-type' => null, + 'content-length' => null, + 'basename' => null + ]; + try { + $url = $request->get('url'); + $basename = pathinfo($url, PATHINFO_BASENAME); + + $guzzle = new Guzzle($url); + $res = $guzzle->head("")->send(); + $response['content-type'] = $res->getContentType(); + $response['content-length'] = $res->getContentLength(); + $response['basename'] = $basename; + } + catch (\Exception $e) { + // no-op : head will return no info but will not crash + } + + return $this->app->json($response); + } + /** * Upload processus * @@ -98,7 +123,7 @@ class UploadController extends Controller 'message' => '', 'element' => '', 'reasons' => [], - 'id' => '', + 'id' => '', ]; if (null === $request->files->get('files')) { @@ -119,18 +144,44 @@ class UploadController extends Controller throw new AccessDeniedHttpException('User is not allowed to add record on this collection'); } + /** @var UploadedFile $file */ $file = current($request->files->get('files')); if (!$file->isValid()) { throw new BadRequestHttpException('Uploaded file is invalid'); } - try { + if ($file->getClientOriginalName() === "blob" && $file->getClientMimeType() === "application/json") { + + // a "upload by url" was done, we receive a tiny json that contains url. + $json = json_decode(file_get_contents($file->getRealPath()), true); + $url = $json['url']; + $pi = pathinfo($url); // filename, extension + + $tempfile = $this->getTemporaryFilesystem()->createTemporaryFile('download_', null, $pi['extension']); + + try { + $guzzle = new Guzzle($url); + $res = $guzzle->get("", [], ['save_to' => $tempfile])->send(); + } + catch (\Exception $e) { + throw new BadRequestHttpException(sprintf('Error "%s" downloading "%s"', $e->getMessage(), $url)); + } + + if($res->getStatusCode() !== 200) { + throw new BadRequestHttpException(sprintf('Error %s downloading "%s"', $res->getStatusCode(), $url)); + } + + $uploadedFilename = $renamedFilename = $tempfile; + + $originalName = $pi['filename'] . '.' . $pi['extension']; + + } else { // Add file extension, so mediavorus can guess file type for octet-stream file $uploadedFilename = $file->getRealPath(); $renamedFilename = null; - if(!empty($this->app['conf']->get(['main', 'storage', 'tmp_files']))){ + if(!empty($this->app['conf']->get(['main', 'storage', 'tmp_files']))) { $tmpStorage = \p4string::addEndSlash($this->app['conf']->get(['main', 'storage', 'tmp_files'])).'upload/'; if(!is_dir($tmpStorage)){ @@ -139,12 +190,16 @@ class UploadController extends Controller $renamedFilename = $tmpStorage. pathinfo($file->getRealPath(), PATHINFO_FILENAME) .'.' . pathinfo($file->getClientOriginalName(), PATHINFO_EXTENSION); - }else{ + } else { $renamedFilename = $file->getRealPath() . '.' . pathinfo($file->getClientOriginalName(), PATHINFO_EXTENSION); } $this->getFilesystem()->rename($uploadedFilename, $renamedFilename); + $originalName = $file->getClientOriginalName(); + } + + try { $media = $this->app->getMediaFromUri($renamedFilename); $collection = \collection::getByBaseId($this->app, $base_id); @@ -153,7 +208,7 @@ class UploadController extends Controller $this->getEntityManager()->persist($lazaretSession); - $packageFile = new File($this->app, $media, $collection, $file->getClientOriginalName()); + $packageFile = new File($this->app, $media, $collection, $originalName); $postStatus = $request->request->get('status'); @@ -184,7 +239,9 @@ class UploadController extends Controller $code = $this->getBorderManager()->process( $lazaretSession, $packageFile, $callback, $forceBehavior); - $this->getFilesystem()->rename($renamedFilename, $uploadedFilename); + if($renamedFilename !== $uploadedFilename) { + $this->getFilesystem()->rename($renamedFilename, $uploadedFilename); + } if (!!$forceBehavior) { $reasons = []; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Upload.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Upload.php index a32133050f..f310d75744 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Upload.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Upload.php @@ -66,6 +66,9 @@ class Upload implements ControllerProviderInterface, ServiceProviderInterface $controllers->get('/html5-version/', 'controller.prod.upload:getHtml5UploadForm') ->bind('upload_html5_form'); + $controllers->get('/head/', 'controller.prod.upload:getHead') + ->bind('upload_head'); + $controllers->post('/', 'controller.prod.upload:upload') ->bind('upload'); diff --git a/templates/web/prod/upload/upload.html.twig b/templates/web/prod/upload/upload.html.twig index ebb5b73922..9aef063080 100644 --- a/templates/web/prod/upload/upload.html.twig +++ b/templates/web/prod/upload/upload.html.twig @@ -31,7 +31,10 @@ {{ 'Select files...' | trans }} -
+
+

{{ "Or" | trans }}

+
+
({% trans with {'%maxFileSizeReadable%' : maxFileSizeReadable} %}maximum : %maxFileSizeReadable%{% endtrans %})