/app/Services/UploadService.php
PHP | 290 lines | 166 code | 33 blank | 91 comment | 16 complexity | bffa82b11a92de33e06b3ca16f6e4150 MD5 | raw file
- <?php
- namespace App\Services;
- use App\Contracts\DocumentRepositoryInterface as Repository;
- use App\Contracts\UploadServiceInterface;
- use App\Jobs\ImageAnalysisJob;
- use App\Libraries\Formatter;
- use App\Models\Document;
- use Illuminate\Http\UploadedFile;
- use Illuminate\Support\Facades\App;
- use Illuminate\Support\Facades\Config;
- use Illuminate\Support\Facades\Storage;
- use Mockery\Exception;
- /**
- * Class Uploader
- * @package App\Libraries
- */
- class UploadService implements UploadServiceInterface
- {
- const EXIF_DATE_TIME_KEY = 'DateTime';
- /**
- * @var string $resource Indica o Subdiretório onde será armazenado o arquivo
- */
- public $resource = 'warehouse';
- /**
- * @var Repository $repository Repositório que irá armazenar as informações sobre os arquivos recebidos
- */
- protected $repository;
- /**
- * @var mixed
- */
- protected $validationService;
- /**
- * @var array $fileList Armazena os arquivos que foram recebidos por esta instância
- */
- private $fileList = [];
- /**
- * Uploader constructor.
- *
- * @param Repository $repository
- */
- public function __construct(Repository $repository)
- {
- $this->repository = $repository;
- $this->validationService = App::make(UploadValidationService::class);
- }
- /**
- * Determina qual será o subdiretório utilizado para armazenar o arquivo
- * @param string $resource
- */
- public function setResource($resource)
- {
- $this->resource = rtrim($resource, '/');
- }
- /**
- * Recebe os dados e determina delega a ação para o método que gerencia um ou para o método
- * que gerencia vários arquivos, dependendo do tipo de dados recebidos.
- *
- * @param mixed $files Dados recebidos no request contendo os arquivos
- * @return Document|array Um objeto Documento ou um array de objetos Documento
- * @throws \Exception Lança uma Exception caso os dados recebidos sejam inválidos
- */
- public function handle($files)
- {
- if (is_array($files)) {
- return $this->handleMultipleFiles($files);
- }
- if (is_object($files)) {
- return $this->handleSingleFile($files);
- }
- throw new \InvalidArgumentException("Dados inválidos para upload");
- }
- /**
- * Trata o caso do recebimento de vários arquivos no mesmo Request
- * @param array $files Arquivos enviados no Request
- * @return array Arquivos recebidos e tratados
- * @throws \Exception
- */
- public function handleMultipleFiles(array $files)
- {
- foreach ($files as $file) {
- $this->handleSingleFile($file);
- }
- return $this->getUploadedFilesList();
- }
- /**
- * Realiza o upload de um único arquivo e retorna um objeto Document com as informações
- * sobre o arquivo que foi recebido
- *
- * @param $file UploadedFile Arquivo recebido no request
- * @return Document Dados gerados pelo upload
- * @throws \Exception Uma Exception é lançada caso os dados recebidos sejam inválidos
- */
- public function handleSingleFile($file)
- {
- if (!$file instanceof UploadedFile) {
- throw new \InvalidArgumentException("Dados inválidos para upload");
- }
- $this->validate($file);
- $document = $this->createDocument($file);
- $disk = Storage::disk()->getDriver();
- $disk->put($document->getPath(), fopen($file, 'r+'), [
- 'visibility' => 'public',
- 'ContentType' => $document->getMimeType()
- ]);
- $this->saveDocument($document);
- $this->addFileToList($document);
- return $document;
- }
- /**
- * @param $file UploadedFile
- * @return bool
- */
- private function validate(UploadedFile $file)
- {
- if ($this->validationService->validateWhitelisted($file)) {
- return $this->validationService->validateResourceUpload($file, $this->resource);
- }
- return false;
- }
- /**
- * Gera um objeto Document a partir de um arquivo recebido
- *
- * @param $file UploadedFile Arquivo tratado no upload
- * @return Document
- * @throws \Exception
- */
- private function createDocument(UploadedFile $file)
- {
- $data = [
- 'id' => $this->getId(),
- 'originalName' => $file->getClientOriginalName(),
- 'extension' => strtolower($file->getClientOriginalExtension()),
- 'size' => $file->getSize(),
- 'mimeType' => $file->getClientMimeType(),
- 'storageDisk' => Config::get('filesystems.default'),
- 'hash' => $file->getRealPath()
- ];
- $data['path'] = sprintf('%s/%s.%s', $this->resource, $data['id'], $data['extension']);
- $data['url'] = implode('/', [env('APP_URL'), $data['path']]);
- try {
- /**
- * se for uma imagem jpg, tentar extrair exif e verificar a orientação
- */
- if ($this->validationService->validateMimeType($file, ['image/jpeg'])) {
- $data['exif'] = $this->extractExifData($file);
- $this->checkAndFixImageOrientation($file);
- }
- } catch (\Exception $e) {
- }
- return new Document($data);
- }
- /**
- * Gera uma string parcialmente randômica para que seja atribuida ao arquivo recebido
- * O primeiro segmento da string é o unix timestamp do momento do upload, os outros
- * segmentos, são randômicos.
- *
- * @return string
- * @example: 1494190264.590f88b83cbca4.57186700
- */
- private function getId()
- {
- return (string)uniqid(sprintf('%s.', time()), true);
- }
- /**
- * @param UploadedFile $file
- * @return array
- */
- private function extractExifData(UploadedFile $file)
- {
- if (!function_exists('exif_read_data')) {
- throw new Exception(trans('messages.error.EXIF_EXT_NOT_INSTALLED'));
- }
- $exif = exif_read_data($file->path());
- $formatter = new Formatter();
- $data = [];
- if (isset($exif["GPSLongitude"]) && isset($exif["GPSLatitude"])) {
- $data['latitude'] = $formatter->gpsDegreesToCoordinates(
- $exif["GPSLatitude"],
- $exif["GPSLatitudeRef"]
- );
- $data['longitude'] = $formatter->gpsDegreesToCoordinates(
- $exif["GPSLongitude"],
- $exif["GPSLongitudeRef"]
- );
- }
- if (isset($exif[self::EXIF_DATE_TIME_KEY]) && !empty($exif[self::EXIF_DATE_TIME_KEY])) {
- $data['dateTimeTaken'] = $exif[self::EXIF_DATE_TIME_KEY];
- }
- return $data;
- }
- /**
- * @param UploadedFile $file
- */
- private function checkAndFixImageOrientation(UploadedFile $file)
- {
- if (!function_exists('exif_read_data')) {
- throw new Exception(trans('messages.error.EXIF_EXT_NOT_INSTALLED'));
- }
- $exif = exif_read_data($file->path());
- if (!empty($exif['Orientation'])) {
- $imageResource = imagecreatefromjpeg($file->path());
- switch ($exif['Orientation']) {
- case 3:
- $image = imagerotate($imageResource, 180, 0);
- break;
- case 6:
- $image = imagerotate($imageResource, -90, 0);
- break;
- case 8:
- $image = imagerotate($imageResource, 90, 0);
- break;
- default:
- $image = $imageResource;
- }
- imagejpeg($image, $file->path(), 90);
- if ($imageResource) {
- imagedestroy($imageResource);
- }
- if ($image) {
- imagedestroy($image);
- }
- }
- }
- /**
- * Salva os dados de um Document utilizando o Repository
- *
- * @param $document
- */
- private function saveDocument(Document $document)
- {
- $entity = $document->toArray();
- //não persistir a url web para evitar acoplamento
- unset($entity['url']);
- $this->repository->save($entity);
- if (env('ENABLE_IMAGE_ANALYSIS', false) &&
- in_array($document->getMimeType(), config('upload.default.images'))) {
- // Se for uma imagem, e a análise estiver habilitada, disparar job de análise
- dispatch(new ImageAnalysisJob($document));
- }
- }
- /**
- * Adiciona na lista de arquivos desta instância um Document representando um arquivo
- * @param $document Document
- */
- public function addFileToList(Document $document)
- {
- $this->fileList[] = $document;
- }
- /**
- * Retorna a lista de de objetos Document desta instância
- * @return array
- */
- public function getUploadedFilesList()
- {
- return $this->fileList;
- }
- }