denyAccessUnlessGranted('ROLE_VIEWER'); $document = $this->documentRepository->find($id); if (!$document) { return $this->json(['error' => 'Document not found.'], 404); } $path = $document->getPath(); // Backward compatibility: serve Base64 data URIs from DB if ($this->storageService->isBase64DataUri($path)) { $parts = explode(',', $path, 2); $content = base64_decode($parts[1] ?? '', true); if (false === $content) { return $this->json(['error' => 'Invalid document data.'], 500); } $disposition = HeaderUtils::makeDisposition(ResponseHeaderBag::DISPOSITION_INLINE, $document->getFilename()); return new Response($content, 200, [ 'Content-Type' => $document->getMimeType(), 'Content-Disposition' => $disposition, 'Content-Length' => (string) strlen($content), 'Cache-Control' => 'private, max-age=3600', 'X-Content-Type-Options' => 'nosniff', 'Content-Security-Policy' => 'sandbox', ]); } // File-based path: serve from disk $absolutePath = $this->storageService->getAbsolutePath($path); if (!file_exists($absolutePath)) { return $this->json(['error' => 'File not found on disk.'], 404); } $response = new BinaryFileResponse($absolutePath); $response->headers->set('Content-Type', $document->getMimeType()); $response->setContentDisposition( ResponseHeaderBag::DISPOSITION_INLINE, $document->getFilename() ); $response->headers->set('Cache-Control', 'private, max-age=3600'); $response->headers->set('X-Content-Type-Options', 'nosniff'); $response->headers->set('Content-Security-Policy', 'sandbox'); return $response; } #[Route('/{id}/download', name: 'document_download_file', methods: ['GET'])] public function download(string $id): Response { $this->denyAccessUnlessGranted('ROLE_VIEWER'); $document = $this->documentRepository->find($id); if (!$document) { return $this->json(['error' => 'Document not found.'], 404); } $path = $document->getPath(); if ($this->storageService->isBase64DataUri($path)) { $parts = explode(',', $path, 2); $content = base64_decode($parts[1] ?? '', true); if (false === $content) { return $this->json(['error' => 'Invalid document data.'], 500); } $disposition = HeaderUtils::makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $document->getFilename()); return new Response($content, 200, [ 'Content-Type' => 'application/octet-stream', 'Content-Disposition' => $disposition, 'Content-Length' => (string) strlen($content), ]); } $absolutePath = $this->storageService->getAbsolutePath($path); if (!file_exists($absolutePath)) { return $this->json(['error' => 'File not found on disk.'], 404); } $response = new BinaryFileResponse($absolutePath); $response->setContentDisposition( ResponseHeaderBag::DISPOSITION_ATTACHMENT, $document->getFilename() ); return $response; } }