UploadController.php 7.57 KiB
<?php
/*
 * Copyright (C) 2015 IRSTEA
 * All rights reserved.
 */
namespace Irstea\FileUploadBundle\Controller;
use Irstea\FileUploadBundle\Entity\UploadedFile;
use Irstea\FileUploadBundle\Http\UploadedFileResponse;
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\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_READ_INTENTION = "uploaded_file_read";
    const CSRF_WRITE_INTENTION = "uploaded_file_write_%s";
    /**
     * @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(
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
$data['name'], $data['size'], $data['type'], isset($data['lastModified']) ? $data['lastModified'] : null ); $token = $this->generateCsrfToken(self::CSRF_WRITE_INTENTION, $file); $parameters = [ 'id' => $file->getId(), 'token' => $token ]; $deleteUrl = $this->router->generate('file_upload_delete', $parameters); return $this->createResponse( Response::HTTP_CREATED, 'New file created', [ 'id' => $file->getId(), 'put_url' => $this->router->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 ] ); } /** * @Route("/{id}/content", name="file_upload_put_content") * @Method("PUT") * @param Request $request * @param UploadedFile $file */ public function putContentAction(Request $request, UploadedFile $file) { $this->validateCsrfToken($request, self::CSRF_WRITE_INTENTION, $file); if(null !== $range = $request->headers->get('Content-Range', null)) { $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); } $offset = $start; $maxlen = 1 + ($end - $start); $complete = $end === ($total-1); } else { $offset = 0; $maxlen = PHP_INT_MAX; $complete = true; } // 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->createResponse(Response::HTTP_OK, 'Chunk received'); }
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
$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->getDisplayName(), 'type' => $file->getMimeType(), 'size' => $file->getSize(), 'icon' => MimeTypeIcon::getMimeTypeIcon($file->getMimeType()), 'url' => $this->router->generate('file_upload_get_content', $parameters), 'delete_type' => 'DELETE', 'delete_url' => $this->router->generate('file_upload_delete', $parameters), ] ] ] ); } /** * @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, self::CSRF_READ_INTENTION); 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) { $this->validateCsrfToken($request, self::CSRF_WRITE_INTENTION, $file); $this->fileManager->delete($file); return $this->createResponse(); } /** * * @param type $intention * @param UploadedFile $file * @return string */ protected function generateCsrfToken($intention, UploadedFile $file = null) {
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
if(null !== $file) { $intention = sprintf($intention, $file->getId()); } return $this->csrfProvider->generateCsrfToken($intention); } /** * * @param Request $request * @param type $intention * @param UploadedFile $file * @throws HttpException */ protected function validateCsrfToken(Request $request, $intention, UploadedFile $file = null) { if(null !== $file) { $intention = sprintf($intention, $file->getId()); } if(!$this->csrfProvider->isCsrfTokenValid($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); } }