An error occurred while loading the file. Please try again.
-
Guillaume Perréal authoredab94b39d
<?php
/*
* Copyright (C) 2015 IRSTEA
* All rights reserved.
*/
namespace Irstea\FileUploadBundle\Controller;
use Irstea\FileUploadBundle\Entity\UploadedFile;
use Irstea\FileUploadBundle\Service\FileManagerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\RouterInterface;
/**
* @Route("/files", service="irstea_file_upload.upload_controller")
*/
class UploadController extends Controller
{
const CSRF_INTENTION = "file_upload";
/**
*
* @var FileManagerInterface
*/
protected $fileManager;
/**
*
* @var RouterInterface
*/
protected $router;
/**
* @var CsrfProviderInterface
*/
protected $csrfProvider;
/**
*
*/
public function __construct(FileManagerInterface $fileManager, RouterInterface $router, CsrfProviderInterface $csrfProvider)
{
$this->fileManager = $fileManager;
$this->router = $router;
$this->csrfProvider = $csrfProvider;
}
/**
* @Route("", name="file_upload_create")
* @Method("POST")
* @param Request $request
*/
public function createAction(Request $request)
{
$data = $request->request->get('file');
$file = $this->fileManager->create(
$data['name'],
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
$data['size'],
$data['type'],
isset($data['lastModified']) ? $data['lastModified'] : null
);
$token = $this->csrfProvider->generateCsrfToken(self::CSRF_INTENTION);
$location = $this->router->generate(
'file_upload_put',
[
'id' => $file->getId(),
'token' => $token
],
RouterInterface::ABSOLUTE_URL
);
return $this->createResponse(
Response::HTTP_CREATED,
'New file created',
[ 'id' => $file->getId(), 'url' => $location ],
[ 'Location' => $location ]
);
}
/**
* @Route("/{id}", name="file_upload_put")
* @Method("PUT")
* @param Request $request
* @param UploadedFile $file
*/
public function putAction(Request $request, UploadedFile $file)
{
$this->validateToken($request);
$range = $request->headers->get('Content-Range');
$final = true;
if($range) {
$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]);
$stream = $file->open('ab');
$stream->seek($start);
$final = $end === ($total-1);
} else {
$stream = $file->open('wb');
}
// Demande un filehandle plutôt que charger le contenu en mémoire
$input = $request->getContent(true);
if(false !== $fileHandler = $stream->cast(STREAM_CAST_AS_STREAM)) {
// Utilise stream_copy_to_stream si le Gaufrette\Stream peut nous retourner un filehandle
stream_copy_to_stream($input, $fileHandler);
} else {
// Sinon fait une copie par blocs (moins performant)
while(!feof($input)) {
$stream->write(fread($input, 8192));
}
}
fclose($input);
$stream->close();
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
if(!$final) {
return $this->createResponse(Response::HTTP_OK, 'Chunk received', ['range' => compact('start', 'end', 'total')]);
}
$this->fileManager->completed($file);
$parameters = [
'id' => $file->getId(),
'token' => $request->query->get('token')
];
return $this->createResponse(
Response::HTTP_OK,
'File uploaded',
[
'files' => [
[
'id' => $file->getId(),
'name' => $file->getOriginalFilename(),
'type' => $file->getMimeType(),
'size' => $file->getSize(),
'url' => $this->router->generate('file_upload_get', $parameters),
'delete_type' => 'DELETE',
'delete_url' => $this->router->generate('file_upload_delete', $parameters),
]
]
]
);
}
/**
* @Route("/{id}", name="file_upload_get")
* @Method("GET")
* @param Request $request
* @param UploadedFile $file
*/
public function getAction(Request $request, UploadedFile $file)
{
$this->validateToken($request);
if(!$file->isValid()) {
throw new NotFoundHttpException();
}
$response = StreamedResponse::create(function() use($file) { echo $file->getContent(); })
->setPrivate()
->setLastModified($file->getLastModified())
->setEtag($file->getChecksum());
$response->headers->add(
[
'Content-Type' => $file->getMimeType(),
'Content-Length' => $file->getSize(),
'Content-Disposition' => $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$file->getOriginalFilename()
),
]
);
$response->isNotModified($request);
return $response;
}
/**
* @Route("/{id}", name="file_upload_delete")
* @Method("DELETE")
* @param Request $request
* @param UploadedFile $file
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
*/
public function deleteAction(Request $request, UploadedFile $file)
{
$this->validateToken($request);
$this->fileManager->delete($file);
return $this->createResponse();
}
/**
*
* @param Request $request
* @throws HttpException
*/
protected function validateToken(Request $request)
{
if(!$this->csrfProvider->isCsrfTokenValid(self::CSRF_INTENTION, $request->query->get('token', null))) {
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;
return new JsonResponse($data, $status, $headers);
}
}