FileController.php 12.5 KB
<?php

namespace App\Http\Controllers\File;

use App\Enums\Common\Code;
use App\Models\File\File;
use App\Models\File\Image as ImageModel;
use App\Models\Project\Project;
use App\Services\CosService;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Cache;

class FileController
{
    public $upload_img = [
        //设置静态缓存参数(304)
        'header' => [
            'Cache-Control' => 'max-age=2592000',
            'Pragma' => 'cache',
            'Expires' => "%Expires%", // cache 1 month
            'etag' => "%etag%",
            'Last-Modified' => "%Last-Modified%",
            'Content-Description' => 'File Transfer',
        ],
    ];

    public $path = '';//路径

    public $config = '';//存储默认配置

    public $request = '';//request

    public $param = '';//参数

    public $token = '';//token

    public $cache = '';//缓存数据
    public $upload_location = 1;
    public $file_type = [
        2 => 'other',//其他
        1 => 'video',//视频
        0 => 'file'//文件
    ];
    public function __construct()
    {
        $this->request = request();
        $this->param = $this->request->all();
        $this->config = config('filesystems.disks.upload');
        $this->uploads = config('upload.default_file');
        $this->token = $this->request->header('token');
        $this->cache = Cache::get($this->token);
    }

    /**
     * @param  :(获取文件)$hash
     * @name   :index
     * @author :lyh
     * @method :post
     * @time   :2023/5/9 9:15
     */
    public function index($hash = '')
    {
        // 检查是否有修改日期或ETag头部
        if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
            header("HTTP/1.1 304 Not Modified");
            exit;
        }
        $file_model = new File();
        $info = $file_model->read(['hash' => $hash]);
        if ($info === false) {
            $this->response('指定文件不存在!', Code::USER_ERROR);
        }
        //获取cos链接
        if($info['is_cos'] == 1){
            $cos = new CosService();
            return $cos->getImageUrl($info['path']);
        }
        $path = $this->config['root'].'/'.$info['path'];
        if (!is_file($path)) {
            $this->response('指定文件已被系统删除!', Code::USER_ERROR);
        }
        $size = $info['size'];
        // 设置Content-Type头部
        if($info['type'] == 'mp4'){
            $header['Content-Type'] = 'video/' . $info['type'];
        }
        // 设置Accept-Ranges头部
        $header['Accept-Ranges'] = 'bytes';
        // 检查是否有范围请求
//        if (isset($_SERVER['HTTP_RANGE'])) {
//            $range = $_SERVER['HTTP_RANGE'];
//            $ranges = explode('-', substr($range, 6));
//            $start = intval($ranges[0]);
//            $end = $size - 1;
//            if (!empty($ranges[1])) {
//                $end = intval($ranges[1]);
//            }
//            $length = $end - $start + 1;
//            // 设置部分响应头部
//            $header['Content-Length'] = $length;
//            $header['Content-Range'] = 'bytes ' . $start . '-' . $end . '/' . $size;
//            // 发送206 Partial Content状态码
//            header('HTTP/1.1 206 Partial Content');
//            header('Status: 206 Partial Content');
//            header('Accept-Ranges: bytes');
//            header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size);
//            // 读取部分内容并发送响应
//            $file = fopen($path, 'rb');
//            fseek($file, $start);
//            $buffer = 1024 * 8; // 设置缓冲区大小
//            while (!feof($file) && ($p = ftell($file)) <= $end) {
//                if ($p + $buffer > $end) {
//                    // 最后一块缓冲区
//                    $buffer = $end - $p + 1;
//                }
//                echo fread($file, $buffer);
//                flush(); // 将输出刷新到浏览器
//            }
//            fclose($file);
//            exit;
//        }
        // 无范围请求,发送完整文件
        $header['Content-Length'] = $size;
        $content = file_get_contents($path);
        // 发送完整响应
        foreach ($header as $name => $value) {
            header("$name: $value");
        }
        echo $content;
        exit;
    }

    /**
     * @remark :上传文件
     * @name   :upload
     * @author :lyh
     * @method :post
     * @time   :2023/6/17 16:32
     */
    public function upload() {
        $this->request->validate([
            'file'=>['required'],
        ],[
            'file.required'=>'必须填写',
        ]);
        $files = $this->request->file('file');

        if (empty($files)) {
            $this->response('没有上传的文件!', 400);
        }
        $type = $this->request->post('type','single');
        $this->setUrl();
        if ($type == 'multi') {
            return $this->multi($files);
        } else {
            $size = $files->getSize();
            $file_type = $files->getClientOriginalExtension();
            return $this->single($files,$size,$file_type);
        }
    }

    /**
     * @param $files
     * @remark :单文件上传
     * @name   :single
     * @author :lyh
     * @method :post
     * @time   :2023/6/17 16:32
     */
    public function single($files,$size,$file_type){
        $hash = hash_file('md5', $files->getPathname());
        //查看文件是否存在
        $fileModel = new File();
        $file_hash = $fileModel->read(['hash'=>$hash]);
        if($file_hash !== false){
            return $this->response('资源',Code::SUCCESS,['file'=>$hash,'file_link'=>$this->getFileUrl($fileModel,$hash)]);
        }
        $url = $this->config['root'].$this->path;
        $fileName = uniqid().rand(10000,99999).'.'.$files->getClientOriginalExtension();
        //同步数据到cos
        if($this->upload_location == 1){
            $cosService = new CosService();
            $cosService->uploadFile($files,$this->path,$fileName);
        }else{
            $res =  $files->move($url,$fileName);
            if ($res === false) {
                return $this->response($files->getError(), Code::USER_ERROR);
            }
        }
        $this->saveMysql($fileModel,$size,$file_type,$fileName,$hash,$this->upload_location);
        return $this->response('资源',Code::SUCCESS,['file'=>$hash,'file_link'=>$this->getFileUrl($fileModel,$hash)]);
    }

    /**
     * @remark :保存数据库
     * @name   :saveMysql
     * @author :lyh
     * @method :post
     * @time   :2023/7/19 16:38
     */
    public function saveMysql(&$fileModel,$size,$image_type,$fileName,$hash,$is_cos = 0){
        $data = [
            'path' => $this->path.'/'.$fileName,
            'created_at' => date('Y-m-d H:i:s',time()),
            'size' => $size,
            'hash' => $hash,
            'type'=>$image_type,
            'refer'=>$this->param['refer'] ?? 1,
            'is_cos'=>$is_cos
        ];
        $rs = $fileModel->add($data);
        if ($rs === false) {
            return $this->response('添加失败', Code::USER_ERROR);
        }
        return true;
    }

    /**
     * @param $files
     * @remark :多文件上传(暂时未用)
     * @name   :multi
     * @author :lyh
     * @method :post
     * @time   :2023/6/17 16:32
     */
    private function multi($files) {
        $data = [];
        foreach ($files as $file) {
            $fileModel = new File();
            $hash = hash_file('md5', $file->getPathname());
            $file_hash = $fileModel->read(['hash'=>$hash]);
            if($file_hash !== false){
                $data[] = ['file'=>$hash,'file_link'=>$this->getFileUrl($fileModel,$hash)];
                continue;
            }
            $url = $this->config['root'].'/'.$this->path;
            $fileName = uniqid().rand(10000,99999).'.'.$files->getClientOriginalExtension();
            //同步数据到cos
            if($this->upload_location == 1){
                $cosService = new CosService();
                $cosService->uploadFile($files,$this->path,$fileName);
            }else{
                $res =  $files->move($url,$fileName);
                if ($res === false) {
                    return $this->response($files->getError(), Code::USER_ERROR);
                }
            }
            $size = $file->getSize();
            $file_type = $file->getClientOriginalExtension();
            $this->saveMysql($fileModel,$size,$file_type,$fileName,$hash,$this->upload_location);
            $data[] = ['file'=>$hash,'file_link'=>$this->getFileUrl($fileModel,$hash)];
        }
        return $this->response('资源',Code::SUCCESS,$data);
    }


    /**
     * @param $msg
     * @param string $code
     * @param $data
     * @param $result_code
     * @param $type
     * @remark :统一返回接口
     * @name   :response
     * @author :lyh
     * @method :post
     * @time   :2023/6/17 16:33
     */
    public function response($msg = null,string $code = Code::SUCCESS,$data = [],$result_code = 200,$type = 'application/json'): JsonResponse
    {
        $code = Code::fromValue($code);
        $result = [
            'msg' => $msg == ' ' ? $code->description : $msg,
            'code' => $code->value,
            'data' => $data,
        ];
        $this->header['Content-Type'] = $type;
        $response =  response($result,$result_code,$this->header);
        throw new HttpResponseException($response);
    }

    /**
     * @remark :文件下载
     * @name   :downLoad
     * @author :lyh
     * @method :post
     * @time   :2023/6/26 16:28
     */
    public function downLoad(){
        $file_model = new File();
        $info = $file_model->read(['hash' => $this->param['hash']]);
        if ($info === false) {
            $this->response('指定文件不存在!', Code::USER_ERROR);
        }

        if($info['is_cos'] == 1){
            $cos = new CosService();
            $fileUrl = $cos->getImageUrl($info['path']);
        }else{
            $fileUrl = $this->config['root'].$info['path'];
            if (!is_file($fileUrl)) {
                $this->response('指定文件已被系统删除!', Code::USER_ERROR);
            }
        }
        $fileName = basename($info['path']);  // 要保存的文件名
        // 设置响应头
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . $fileName . '"');
        // 下载文件
        readfile($fileUrl);
    }

    /**
     * @remark :保存路径
     * @name   :setUrl
     * @author :lyh
     * @method :post
     * @time   :2023/7/18 15:36
     */
    public function setUrl(){
        //A端上传
        if(isset($this->param['refer_type']) && $this->param['refer_type'] == 1){
            $this->path = $this->uploads['path_a'].'/'.$this->file_type[$this->param['refer']].'/'.date('Y-m');
        }else{
            //B端上传
            if(isset($this->param['upload_method'])  && $this->param['upload_method'] == 1){
                //强制上传本地配置
                $this->upload_location = 0;
            }else{
                //根据项目上传标识区分上传到cos/本地
                $projectModel = new Project();
                $project_info = $projectModel->read(['id'=>$this->cache['project_id']],['upload_location']);
                $this->upload_location = $project_info['upload_location'];
            }
            $this->path = $this->uploads['path_b'].'/'.$this->cache['project_id'].'/'.$this->file_type[$this->param['refer']].'/'.date('Y-m');
        }
    }

    /**
     * @remark :获取文件链接
     * @name   :getImageUrl
     * @author :lyh
     * @method :post
     * @time   :2023/7/20 16:46
     */
    public function getFileUrl(&$fileModel,$hash){

        $info = $fileModel->read(['hash'=>$hash]);
        if($info['is_cos'] == 1){
            $cos = new CosService();
            $url = $cos->getImageUrl($info['path']);
        }else{
            $url = url('a/file/'.$info['hash']);
        }
        return $url;
    }

    /**
     * @remark :获取所有视频
     * @name   :getImageList
     * @author :lyh
     * @method :post
     * @time   :2023/6/29 11:48
     */
    public function getFileList(){
        $fileModel = new File();
        $lists = $fileModel->list(['refer'=>1],$order = 'id',['id','hash','type','path','created_at']);
        foreach ($lists as $k => $v){
            $v['file_link'] = $this->getFileUrl($fileModel,$v['hash']);
            $lists[$k] = $v;
        }
        $this->response('success',Code::SUCCESS,$lists);
    }
}