300 lines
8.2 KiB
PHP
300 lines
8.2 KiB
PHP
<?php
|
||
|
||
namespace App\Domains\File\Services;
|
||
|
||
use Carbon\Carbon;
|
||
use App\Core\Service;
|
||
use Illuminate\Http\Request;
|
||
use App\Models\Account\Account;
|
||
use Illuminate\Http\UploadedFile;
|
||
use App\Exceptions\BeUsedException;
|
||
use Intervention\Image\Facades\Image;
|
||
use App\Models\File\File as FileModel;
|
||
use App\Exceptions\NotAllowedException;
|
||
use Illuminate\Support\Facades\Storage;
|
||
use App\Exceptions\UploadFailedException;
|
||
use App\Domains\File\Repositories\FileRepository;
|
||
use App\Models\File\FileWith as FileFileWithModel;
|
||
use App\Domains\File\Repositories\FileWithRepository;
|
||
|
||
class FileService extends Service
|
||
{
|
||
protected $user;
|
||
|
||
protected $fileRepository;
|
||
|
||
protected $fileWithRepository;
|
||
|
||
public static $types = ['account/avatar'];
|
||
|
||
public static $mimeTypes = [
|
||
'image' => ['image/jpeg', 'image/gif', 'image/bmp', 'image/png'],
|
||
'audio' => ['audio/amr', 'audio/aac', 'audio/ogg', 'audio/mpeg'],
|
||
'video' => ['video/mp4', 'video/mpeg', 'video/x-flv', 'video/3gpp', 'video/quicktime', 'video/mov','video/x-m4v']
|
||
];
|
||
|
||
public static $extensions = [
|
||
'image' => ['png', 'jpg', 'jpeg', 'gif', 'bmp'],
|
||
'audio' => ['amr', 'ogg', 'mpeg'],
|
||
'video' => ['mp4', 'mpeg', 'flv', '3gp', 'mov']
|
||
];
|
||
|
||
/**
|
||
* 构造函数
|
||
*
|
||
* @return void
|
||
*/
|
||
public function __construct(Request $request, FileRepository $fileRepository, FileWithRepository $fileWithRepository)
|
||
{
|
||
$this->user = $request->user();
|
||
$this->fileRepository = $fileRepository;
|
||
$this->fileWithRepository = $fileWithRepository;
|
||
}
|
||
|
||
/**
|
||
* 文件列表
|
||
*
|
||
* @param array $conditions
|
||
* @return mixed
|
||
*/
|
||
public function index(array $conditions = [])
|
||
{
|
||
$limit = $conditions['limit'] ?? 20;
|
||
return $this->fileRepository->with('file_withs')->applyConditions()->paginate($limit);
|
||
}
|
||
|
||
/**
|
||
* 删除
|
||
*
|
||
* @return bool
|
||
*/
|
||
public function destroy($ids)
|
||
{
|
||
$ids = is_array($ids) ? $ids : [$ids];
|
||
|
||
if ($this->fileWithRepository->whereIn('file_id', $ids)->count()) {
|
||
throw new BeUsedException('文件已被使用, 不能删除');
|
||
}
|
||
|
||
$this->fileRepository->destroy($ids);
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 使用type, type_id查找文件
|
||
*
|
||
* @param string $type
|
||
* @param string|int $typeIds
|
||
* @return void
|
||
*/
|
||
public function fetchByType($type, $typeIds, $force = false)
|
||
{
|
||
if ($force) {
|
||
$this->fileWithRepository->forgetCached();
|
||
}
|
||
|
||
$files = $this->fileWithRepository->with('file')->withType($type, $typeIds)->get();
|
||
|
||
$files->map(function ($item) {
|
||
if ($file = $item->file) {
|
||
$file->url = cdn($item->disk, $item->filename, $item->created_at);
|
||
$file->cover = $item->thumb ? cdn($item->disk, $this->generateFilePath($item->filename), $item->created_at) : '';
|
||
$item->file = $file;
|
||
}
|
||
});
|
||
|
||
return $files;
|
||
}
|
||
|
||
/**
|
||
* 储存上传文件
|
||
*
|
||
* @param UploadedFile $file
|
||
*
|
||
* @throws UploadFailedException
|
||
* @return mixed
|
||
*/
|
||
public function upload(UploadedFile $file, $input = [], $disk = null)
|
||
{
|
||
$disk = $disk ?: config('filesystems.default');
|
||
|
||
if ($input['filename']) {
|
||
$fileModel = $this->store($file, $input, $disk, true);
|
||
} else {
|
||
$fileModel = $this->validateFileInDatabase($file, function (UploadedFile $file, string $md5) use ($input, $disk) {
|
||
$input['filename'] = $this->generateFilePath($md5);
|
||
return $this->store($file, $input, $disk);
|
||
});
|
||
|
||
if ($input['type'] && $input['typeid'] && in_array($input['type'], self::$types)) {
|
||
$this->apply($fileModel->id, $input['type'], $input['typeid'], $input['cover'] ?? false);
|
||
}
|
||
}
|
||
|
||
|
||
return $fileModel;
|
||
}
|
||
|
||
/**
|
||
* 添加文件关联
|
||
*
|
||
* @param array|int $ids
|
||
* @param string $type
|
||
* @param int|string $typeid
|
||
* @return void
|
||
*/
|
||
public function apply($ids, $type, $typeid, $cover = false)
|
||
{
|
||
if (!in_array($type, self::$types)) {
|
||
throw new NotAllowedException('不允许的上传类型');
|
||
}
|
||
|
||
$ids = is_array($ids) ? $ids : [$ids];
|
||
|
||
if (empty($ids)) {
|
||
return false;
|
||
}
|
||
|
||
if ($this->user instanceof Account) {
|
||
$column = 'account_id';
|
||
}
|
||
|
||
$news = [];
|
||
|
||
$existed_ids = $this->fileRepository->select('id')->whereIn('id', $ids)->get()->pluck('id')->toArray();
|
||
|
||
foreach ($ids as $id) {
|
||
if (!in_array($id, $existed_ids)) {
|
||
continue;
|
||
}
|
||
|
||
$new = [
|
||
'file_id' => $id,
|
||
'account_id' => 0,
|
||
'type' => $type,
|
||
'type_id' => $typeid,
|
||
'created_at' => date('Y-m-d H:i:s'),
|
||
'updated_at' => date('Y-m-d H:i:s'),
|
||
];
|
||
|
||
if ($column) {
|
||
$new[$column] = $this->user->id;
|
||
}
|
||
|
||
$news[] = $new;
|
||
}
|
||
|
||
if (empty($news)) {
|
||
return false;
|
||
}
|
||
|
||
if ($cover) {
|
||
$this->detach($type, $typeid);
|
||
}
|
||
|
||
return $this->fileWithRepository->insert($news);
|
||
}
|
||
|
||
/**
|
||
* 取消文件关联
|
||
*
|
||
* @param string $type
|
||
* @param int|string $typeid
|
||
* @return boolean
|
||
*/
|
||
public function detach($type, $typeid)
|
||
{
|
||
$this->fileWithRepository->withType($type, $typeid)->delete();
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 缩略图文件名
|
||
*
|
||
* @param string $filename
|
||
* @return string
|
||
*/
|
||
public static function getThumb($filename)
|
||
{
|
||
return $filename.'.thumb.jpg';
|
||
}
|
||
|
||
/**
|
||
* 验证文件是否已上传过,返回FileModel实例
|
||
*
|
||
* @param UploadedFile $file
|
||
* @param callable $call
|
||
* @return FileModel
|
||
*/
|
||
protected function validateFileInDatabase(UploadedFile $file, callable $call): ?FileModel
|
||
{
|
||
$hash = md5_file($file->getRealPath());
|
||
|
||
return $this->fileRepository->where('hash', $hash)->firstOr(function () use ($file, $call, $hash) {
|
||
return call_user_func_array($call, [$file, $hash]);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 存储文件
|
||
*
|
||
* @param UploadedFile $file
|
||
* @param array $input
|
||
* @param string $disk
|
||
* @return mixed
|
||
*/
|
||
protected function store(UploadedFile $file, $input, $disk, $cover = false)
|
||
{
|
||
$disk = $disk ?: config('filesystems.default');
|
||
$clientHeight = $input['height'] ?? 0;
|
||
$clientWidth = $input['width'] ?? 0;
|
||
$clientDuration = $input['duration'] ?? 0;
|
||
|
||
$hash = md5_file($file->getRealPath());
|
||
$path = $input['filename'] ?? $this->generateFilePath($hash);
|
||
|
||
// 图片做旋转处理
|
||
if (in_array($file->getClientMimeType(), ['image/jpeg', 'image/bmp', 'image/png'])) {
|
||
ini_set('memory_limit', '-1');
|
||
try {
|
||
Image::make($file->getRealPath())->orientate()->save($file->getRealPath(), 100);
|
||
} catch (\Exception $e) {
|
||
}
|
||
}
|
||
|
||
list($width, $height) = ($imageInfo = @getimagesize($file->getRealPath())) === false ? [null, null] : $imageInfo;
|
||
|
||
if (($filename = $file->storeAs($path, '', $disk)) === false) {
|
||
throw new UploadFailedException('上传失败');
|
||
}
|
||
|
||
$new = [
|
||
'hash' => $hash,
|
||
'disk' => $disk,
|
||
'filename' => $filename,
|
||
'origin_filename' => $file->getClientOriginalName(),
|
||
'mimetype' => $file->getClientMimeType(),
|
||
'filesize' => $file->getClientSize(),
|
||
'width' => $width ?? $clientWidth,
|
||
'height' => $height ?? $clientHeight,
|
||
'duration' => $clientDuration,
|
||
];
|
||
|
||
$fileModel = $cover ? new FileModel($new) : $this->fileRepository->create($new);
|
||
|
||
return $fileModel;
|
||
}
|
||
|
||
/**
|
||
* 根据文件md5生成文件路径
|
||
*
|
||
* @param string $hash
|
||
* @return void
|
||
*/
|
||
protected function generateFilePath($hash)
|
||
{
|
||
return Carbon::now()->format('Y/m/d') . $hash;
|
||
}
|
||
}
|