An error occurred while loading the file. Please try again.
-
216ad121
<?php declare(strict_types=1);
/*
* Copyright (C) 2015-2017 IRSTEA
* All rights reserved.
*/
namespace Irstea\FileUploadBundle\Controller;
use Irstea\FileUploadBundle\Entity\UploadedFile;
use Irstea\FileUploadBundle\Exception\RejectedFileException;
use Irstea\FileUploadBundle\Http\UploadedFileResponse;
use Irstea\FileUploadBundle\Model\FileManagerInterface;
use Irstea\FileUploadBundle\Model\UploadedFileInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Templating\EngineInterface;
/**
* @Route("/api/files", service="irstea_file_upload.upload_controller")
*/
class UploadController extends Controller
{
public const CSRF_INTENTION = 'uploaded_file';
/**
* @var FileManagerInterface
*/
protected $fileManager;
/**
* @var UrlGeneratorInterface
*/
protected $urlGenerator;
/**
* @var CsrfTokenManagerInterface
*/
protected $csrfTokenManager;
/**
* @var TokenStorageInterface
*/
protected $tokenStorage;
/**
* @var EngineInterface
*/
protected $templating;
/**
* UploadController constructor.
*
* @param FileManagerInterface $fileManager
* @param UrlGeneratorInterface $urlGenerator
* @param CsrfTokenManagerInterface $csrfTokenManager
* @param TokenStorageInterface $tokenStorage
* @param EngineInterface $templating
*/
public function __construct(
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
FileManagerInterface $fileManager,
UrlGeneratorInterface $urlGenerator,
CsrfTokenManagerInterface $csrfTokenManager,
TokenStorageInterface $tokenStorage,
EngineInterface $templating
) {
$this->fileManager = $fileManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
$this->tokenStorage = $tokenStorage;
$this->templating = $templating;
}
/**
* @Route("", name="file_upload_create")
* @Method("POST")
* @param Request $request
*
* @return JsonResponse
*/
public function createAction(Request $request)
{
try {
$data = $request->request->get('file');
$token = $this->tokenStorage->getToken();
$file = $this->fileManager->create(
$data['name'],
$data['size'],
$data['type'],
$data['lastModified'] ?? null,
null !== $token ? $token->getUsername() : null,
$request->getClientIp()
);
$parameters = ['id' => $file->getId()];
$deleteUrl = $this->urlGenerator->generate('file_upload_delete', $parameters);
return $this->createResponse(
Response::HTTP_CREATED,
'New file created',
array_merge(
$file->toArray(),
[
'put_url' => $this->urlGenerator->generate('file_upload_put_content', $parameters),
'delete_type' => 'DELETE',
'delete_url' => $deleteUrl,
]
),
// On a pas de get pour l'instant, le DELETE et ce qui y ressemble le plus
['Location' => $deleteUrl]
);
} catch (\Exception $ex) {
return $this->createExceptionResponse($ex);
}
}
/**
* @Route("/{id}/content", name="file_upload_put_content")
* @Method("PUT")
* @param Request $request
* @param UploadedFile $file
*/
public function putContentAction(Request $request, UploadedFile $file)
{
try {
$this->validateCsrfToken($request);
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
list($offset, $maxlen, $complete) = $this->handleRangeHeader($request);
// Demande un filehandle plutôt que charger le contenu en mémoire
$input = $request->getContent(true);
$file->copyFrom($input, $maxlen, $offset);
fclose($input);
if ($complete) {
return $this->completeUpload($file);
}
return $this->createResponse(Response::HTTP_OK, 'Chunk received');
} catch (\Exception $ex) {
return $this->createExceptionResponse($ex);
}
}
/**
* @param Request $request
*
* @return array
*/
protected function handleRangeHeader(Request $request)
{
if (null === $range = $request->headers->get('Content-Range', null)) {
return [0, PHP_INT_MAX, true];
}
$matches = [];
if (!preg_match('@^bytes (\d+)-(\d+)/(\d+)$@', $range, $matches)) {
throw new BadRequestHttpException('Invalid Content-Range');
}
$start = intval($matches[1]);
$end = intval($matches[2]);
$total = intval($matches[3]);
if ($start < 0 || $start >= $end || $end >= $total) {
throw new HttpException(Response::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE);
}
return [$start, 1 + ($end - $start), $end === ($total - 1)];
}
/**
* @param UploadedFileInterface $file
*
* @return Response
*/
protected function completeUpload(UploadedFileInterface $file)
{
try {
$this->fileManager->completed($file);
} catch (RejectedFileException $ex) {
return $this->createResponse(Response::HTTP_FORBIDDEN, 'File rejected: ' . $ex->getMessage());
}
$parameters = ['id' => $file->getId()];
$fileData = array_merge(
$file->toArray(),
[
'url' => $this->urlGenerator->generate('file_upload_get_content', $parameters),
'delete_type' => 'DELETE',
'delete_url' => $this->urlGenerator->generate('file_upload_delete', $parameters),
'repr' => $this->templating->render(
'IrsteaFileUploadBundle:Extension:uploaded_file.html.twig',
['file' => $file->toArray()]
),
]
);
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
return $this->createResponse(Response::HTTP_OK, 'File uploaded.', ['files' => [$fileData]]);
}
/**
* @Route("/{id}/content", name="file_upload_get_content")
* @Method("GET")
* @param Request $request
* @param UploadedFile $file
*/
public function getContentAction(Request $request, UploadedFile $file)
{
$this->validateCsrfToken($request);
if (!$file->isValid()) {
throw new NotFoundHttpException();
}
$response = UploadedFileResponse::create($file, 200, [], false, ResponseHeaderBag::DISPOSITION_ATTACHMENT);
$response->isNotModified($request);
return $response;
}
/**
* @Route("/{id}", name="file_upload_delete")
* @Method("DELETE")
* @param Request $request
* @param UploadedFile $file
*/
public function deleteAction(Request $request, UploadedFile $file)
{
try {
$this->validateCsrfToken($request);
$this->fileManager->delete($file);
return $this->createResponse();
} catch (\Exception $ex) {
return $this->createExceptionResponse($ex);
}
}
/**
* @param Request $request
*
* @throws HttpException
*/
protected function validateCsrfToken(Request $request)
{
$token = $this->csrfTokenManager->getToken($request->query->get('token', null));
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid CSRF token');
}
}
/**
* @param int $status
* @param string $message
* @param array $data
* @param array $headers
*
* @return JsonResponse
*/
protected function createResponse($status = Response::HTTP_OK, $message = 'OK', array $data = [], array $headers = [])
{
$data['status'] = $status;
$data['message'] = $message;
$response = new JsonResponse($data, $status, $headers);
$response->setStatusCode($status, $message);
281282283284285286287288289290291292293294295296297298299300301302303304305306307
return $response;
}
/**
* @param \Exception $ex
*
* @return JsonResponse
*/
protected function createExceptionResponse(\Exception $ex)
{
return $this->createResponse(
$ex instanceof HttpException ? $ex->getStatusCode() : Response::HTTP_INTERNAL_SERVER_ERROR,
preg_replace("/[\n\r]+/", ' ', $ex->getMessage()),
[
'exception' => [
'class' => get_class($ex),
'file' => $ex->getFile(),
'line' => $ex->getLine(),
'code' => $ex->getCode(),
'trace' => $ex->getTrace(),
],
]
);
}
}