• Laravel5.1 搭建博客 --上传文件及文件管理


    教程源自:Laravel学院

    这一节 咱来说说上传文件的功能实现,我们会把上传的文件保存到项目本地,不仅上传 还有删除和预览功能。


    1 配置

    我们先从配置开始做起,先修改我们自己创建的 blog.php

    <?php
    return [
        'title' => "Larger K's Blog",
        'posts_per_page' => 10,
        'uploads' => [
            'storage' => 'local',       // 储存处
            'webpath' => '/uploads'     // 储存路径
        ],
    ];

    然后编辑 config/filesystems.php

        'disks' => [
    
            'local' => [
                'driver' => 'local',
    //            'root'   => storage_path('app'),
                'root'  => public_path('uploads'),
            ],

    2 创建文件服务类

    我们需要创建一个Manager来封装需要用到的功能。

    2.1 引入dflydev

    difydev包是主要用于分别MIME类型的,我们需要根据不同的文件类型进行不同的操作,所以需要使用到它,如果你记不住它的名字可以到Packagist 中搜索 MIME

    然后我们使用 composer 引入它:

    composer require "dflydev/apache-mime-types"

    2.2 创建UploadsManager

    我们在 app/Service 目录下创建:

    <?php
    namespace AppServices;
    
    use CarbonCarbon;
    use DflydevApacheMimeTypesPhpRepository;
    use IlluminateSupportFacadesStorage;
    use phpDocumentorReflectionDocBlockTagsReturn_;
    
    class UploadsManager {
        protected $disk;
        protected $mimeDelect;
    
        /**
         * UploadsServices constructor.
         * @param $mimeDelect
         */
        public function __construct(PhpRepository $mimeDelect)
        {
            $this->mimeDelect = $mimeDelect;
            $this->disk = Storage::disk(config('blag.uploads.storage'));
        }
    
        /**
         * 返回目录详情
         *
         * @param $folder
         * @return array [
         *      'folder',           文件夹的路径
         *      'folderName',       文件夹名称
         *      'breadcrumbs',      文件夹被分割后的数组
         *      'subfolders',       此文件夹下的所有子文件夹
         *      'files'             此文件夹下的所有文件详情
         * ]
         */
        public function folderInfo($folder)
        {
            // 处理$folder
            $folder = $this->cleanFolder($folder);
            // 获得路径数组
            $breadcrumbs = $this->breadcrumbs($folder);
            // 取出最后一段
            $slice = array_slice($breadcrumbs, -1);
            // 获得当前数组指针下的value
            $folderName = current($slice);
            // 获取0到倒数第二个的片段
            $breadcrumbs = array_slice($breadcrumbs, 0, -1);
            // 获得子目录
            $subfolders = [];
            foreach (array_unique($this->disk->directories($folder)) as $subfolder) {
                $subfolders["/$subfolder"] = basename($subfolder);
            }
            // 获得文件
            $files = [];
            foreach ($this->disk->files($folder) as $path) {
                $files[] = $this->fileDetails($path);
            }
    
            return compact(
                'folder',
                'folderName',
                'breadcrumbs',
                'subfolders',
                'files'
            );
        }
    
        /**
         * 去除路径中首尾的..和/
         *
         * @param string $folder
         * @return string
         * 例子:传入 ../public/images/home/01/ 返回 "/public/images/home/01"
         */
        protected function cleanFolder($folder)
        {
            return '/' . trim(str_replace('..', '', $folder), '/');
        }
    
        /**
         * 返回路径数组
         *
         * @param $folder
         * @return array when $folder is empty[
         *      '/' => 'root'
         * ] when folder is not empty[
         *      "/" => "root"
         *      "/public" => "public"
         *      "/images" => "images"
         *      "/home" => "home"
         * ]
         */
        protected function breadcrumbs($folder)
        {
            // 去除首尾的 /
            $folder = trim($folder, '/');
            $crumbs = ['/' => 'root'];
            if (empty($folder)){
                return $crumbs;
            }
            // 分割路径
            $folders = explode('/', $folder);
            // build
            $build = '';
            foreach ($folders as $folder) {
                $build = '/'.$folder;
                $crumbs[$build] = $folder;
            }
            return $crumbs;
        }
    
        /**
         * 返回文件的详情信息
         *
         * @param string $path
         * @return array
         */
        protected function fileDetails($path)
        {
            $path = '/' . trim($path, '/');
            return [
                'name' => basename($path),
                'fullpath' => $path,
                'webPath' => $this->fileWebpath($path),
                'mimeType' => $this->fileMimeType($path),
                'size' => $this->fileSize($path),
                'modified' => $this->fileModified($path),
            ];
        }
    
        /**
         * 返回文件的Web路径
         *
         * @param $path
         * @return string
         */
        protected function fileWebpath($path)
        {
            $path = rtrim(config('blog.uploads.webpath'), '/') . '/' . ltrim($path, '/');
            return url($path);
        }
    
        /**
         * 返回文件的MIME类型
         *
         * @param $path
         * @return mixed|null|string
         */
        protected function fileMimeType($path)
        {
            return $this->mimeDelect->findType(
                pathinfo(
                    $path, PATHINFO_EXTENSION
                )
            );
        }
    
        /**
         * 返回文件的大小
         *
         * @param $path
         * @return mixed
         */
        protected function fileSize($path)
        {
            return $this->disk->size($path);
        }
    
        /**
         * 返回最后一次被修改的时间
         *
         * @param $path
         * @return static
         */
        protected function fileModified($path)
        {
            return Carbon::createFromTimestamp(
                $this->disk->lastModified($path)
            );
        }
    }

    注释写的很尽力了,敲一遍你就懂了,上面的代码主要是folderInfo这个方法 它返回我们需要用到的一些数据

    2.3 创建帮助函数

    其实把Manager创建好后就可以展示视图了,但是方便我们使用先来创建两个帮助函数,在app目录下创建一个helper.php

    <?php
    /**
     * 返回可读性更好的文件大小
     *
     * @param $bytes
     * @param int $decimals
     * @return string
     */
    function human_filesize($bytes, $decimals = 2){
        $size = ['B', 'kB', 'MB', 'GB', 'TB', 'PB'];
        $factor = floor((strlen($bytes) - 1) / 3);
    
        return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) .@$size[$factor];
    }
    
    /**
     * 判断mime type是否是图片类型
     *
     * @param $mime_type
     * @return bool
     */
    function is_image($mime_type){
        return starts_with($mime_type, 'image/');
    }

     现在有个问题,我们如何在不import的情况下使用到帮助函数呢 它并不是一个类 没有命名空间,解决这个方法就要修改下composer.json文件让它自动加载文件:

       "autoload": {
            "classmap": [
                "database"
            ],
            "psr-4": {
                "App\": "app/"
            },
            "files": [
                "app/helper.php"
            ]

    最后执行以下命令:

    composer dumpauto

    3 展示文件视图

    3.1 首先编辑UploadController的index方法:

    class UploadController extends Controller
    {
        protected $manager;
    
        /**
         * UploadController constructor.
         * @param UploadsManager $manager
         */
        public function __construct(UploadsManager $manager)
        {
            $this->manager = $manager;
        }
    
        public function index(Request $request)
        {
            // 取到目录的详情
            $data = $this->manager->folderInfo($request->get('folder'));
            return view('admin.upload.index', $data);
        }
    }

    创建一个 /admin/upload/index.blade.php

    @extends('admin.layout')
    
    @section('content')
    <div class="container-fluid">
    
        {{-- 顶部工具栏 --}}
        <div class="row page-title-row">
            <div class="col-md-6">
                <h3 class="pull-left">Uploads </h3>
                <div class="pull-left">
                    <ul class="breadcrumb">
                        @foreach ($breadcrumbs as $path => $disp)
                            <li><a href="/admin/upload?folder={{ $path }}">{{ $disp }}</a></li>
                        @endforeach
                        <li class="active">{{ $folderName }}</li>
                    </ul>
                </div>
            </div>
            <div class="col-md-6 text-right">
                <button type="button" class="btn btn-success btn-md" data-toggle="modal" data-target="#modal-folder-create">
                    <i class="fa fa-plus-circle"></i> New Folder
                </button>
                <button type="button" class="btn btn-primary btn-md" data-toggle="modal" data-target="#modal-file-upload">
                    <i class="fa fa-upload"></i> Upload
                </button>
            </div>
        </div>
    
        <div class="row">
            <div class="col-sm-12">
    
                @include('admin.partials.error')
                @include('admin.partials.success')
    
                <table id="uploads-table" class="table table-striped table-bordered">
                    <thead>
                    <tr>
                        <th>Name</th>
                        <th>Type</th>
                        <th>Date</th>
                        <th>Size</th>
                        <th data-sortable="false">Actions</th>
                    </tr>
                    </thead>
                    <tbody>
    
                    {{-- 子目录 --}}
                    @foreach ($subfolders as $path => $name)
                        <tr>
                            <td>
                                <a href="/admin/upload?folder={{ $path }}">
                                    <i class="fa fa-folder fa-lg fa-fw"></i>
                                    {{ $name }}
                                </a>
                            </td>
                            <td>Folder</td>
                            <td>-</td>
                            <td>-</td>
                            <td>
                                <button type="button" class="btn btn-xs btn-danger" onclick="delete_folder('{{ $name }}')">
                                    <i class="fa fa-times-circle fa-lg"></i>
                                    Delete
                                </button>
                            </td>
                        </tr>
                    @endforeach
    
                    {{-- 所有文件 --}}
                    @foreach ($files as $file)
                        <tr>
                            <td>
                                <a href="{{ $file['webPath'] }}">
                                    @if (is_image($file['mimeType']))
                                        <i class="fa fa-file-image-o fa-lg fa-fw"></i>
                                    @else
                                        <i class="fa fa-file-o fa-lg fa-fw"></i>
                                    @endif
                                    {{ $file['name'] }}
                                </a>
                            </td>
                            <td>{{ $file['mimeType'] or 'Unknown' }}</td>
                            <td>{{ $file['modified']->format('j-M-y g:ia') }}</td>
                            <td>{{ human_filesize($file['size']) }}</td>
                            <td>
                                <button type="button" class="btn btn-xs btn-danger" onclick="delete_file('{{ $file['name'] }}')">
                                    <i class="fa fa-times-circle fa-lg"></i>
                                    Delete
                                </button>
                                @if (is_image($file['mimeType']))
                                    <button type="button" class="btn btn-xs btn-success" onclick="preview_image('{{ $file['webPath'] }}')">
                                        <i class="fa fa-eye fa-lg"></i>
                                        Preview
                                    </button>
                                @endif
                            </td>
                        </tr>
                    @endforeach
    
                    </tbody>
                </table>
    
            </div>
        </div>
    </div>
    
    @include('admin.upload._modal')
    @endsection
    
    @section('scripts')
        <script>
            $(function(){
                $("#uploads-table").DataTable();
            })
        </script>
    @endsection

    经过上面的代码,我们可以看到展示效果,你可以自己在upload目录下创建几个文件夹和文件试试。

    下面我们继续写我们 include的 _modal

    {{--创建目录--}}
    <div class="modal fade" id="modal-folder-create">
        <div class="modal-dialog">
            <div class="modal-content">
                <form action="/admin/upload/folder" method="post" class="form-horizontal">
                    <input type="hidden" name="_token" value="{{ csrf_token() }}">
                    {{--隐式传递文件夹--}}
                    <input type="hidden" name="folder" value="{{ $folder }}">
    
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal">
                            x
                        </button>
                        <h4 class="modal-title">Create New Folder</h4>
                    </div>
                    <div class="modal-body">
                        <div class="form-group">
                            <label for="new_folder_name" class="col-sm-3 control-label">Folder Name</label>
                            <div class="col-sm-8">
                                <input type="text" id="new_folder_name" class="form-control" name="new_folder">
                            </div>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
                        <button type="submit" class="btn btn-primary">Create Folder</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
    
    {{-- 删除目录 --}}
    <div class="modal fade" id="modal-folder-delete">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal">
                        ×
                    </button>
                    <h4 class="modal-title">Please Confirm</h4>
                </div>
                <div class="modal-body">
                    <p class="lead">
                        <i class="fa fa-question-circle fa-lg"></i>
                        Are you sure you want to delete the
                        <kbd><span id="delete-folder-name1">folder</span></kbd>
                        folder?
                    </p>
                </div>
                <div class="modal-footer">
                    <form method="POST" action="/admin/upload/folder">
                        <input type="hidden" name="_token" value="{{ csrf_token() }}">
                        <input type="hidden" name="_method" value="DELETE">
                        <input type="hidden" name="folder" value="{{ $folder }}">
                        <input type="hidden" name="del_folder" id="delete-folder-name2">
                        <button type="button" class="btn btn-default" data-dismiss="modal">
                            Cancel
                        </button>
                        <button type="submit" class="btn btn-danger">
                            Delete Folder
                        </button>
                    </form>
                </div>
            </div>
        </div>
    </div>
    
    {{--删除文件--}}
    <div class="modal fade" id="modal-file-delete">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <div class="modal-title">
                        <button type="button" class="close" data-dismiss="modal">
                            x
                        </button>
                        <h4 class="modal-title">Please Confirm</h4>
                    </div>
                </div>
                <div class="modal-body">
                    <p class="lead">
                        <i class="fa fa-question-circle fa-lg"></i>
                        Are you sure your want to delete the <kbd><span id="delete-file-name1"></span></kbd>
                        file?
                    </p>
                </div>
                <div class="modal-footer">
                    <form action="/admin/upload/file" method="post">
                        <input type="hidden" name="_token" value="{{ csrf_token() }}">
                        <input type="hidden" name="_method" value="DELETE">
                        <input type="hidden" name="folder" value="{{ $folder }}">
                        <input type="hidden" name="del_file" id="delete-file-name2">
                        <button type="button" class="btn btn-default" data-dismiss="modal">
                            Cancel
                        </button>
                        <button type="submit" class="btn btn-danger">
                            Delete File
                        </button>
                    </form>
                </div>
            </div>
        </div>
    </div>
    
    {{--上传文件--}}
    <div class="modal fade" id="modal-file-upload">
        <div class="modal-dialog">
            <div class="modal-content">
                <form action="/admin/upload/file" method="post" class="form-horizontal" enctype="multipart/form-data">
                    <input type="hidden" name="_token" value="{{ csrf_token() }}">
                    <input type="hidden" name="folder" value="{{ $folder }}">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal">
                            x
                        </button>
                        <h4 class="modal-title">Upload New Folder</h4>
                    </div>
                    <div class="modal-body">
                        <div class="form-group">
                            <label for="file" class="control-label col-sm-3">File</label>
                            <div class="col-sm-8">
                                <input type="file" id="file" name="file">
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="file_name" class="control-label col-sm-3">Optional Filename</label>
                            <div class="col-sm-4">
                                <input type="text" id="file_name" name="file_name" class="form-control">
                            </div>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button class="btn btn-default" data-dismiss="modal" type="button">
                            Cancel
                        </button>
                        <button class="btn btn-primary" type="submit">
                            Upload File
                        </button>
                    </div>
                </form>
            </div>
        </div>
    </div>
    
    {{--浏览图片--}}
    <div class="modal fade" id="modal-image-view">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal">
                        x
                    </button>
                    <h4 class="modal-title">Image Preview</h4>
                </div>
                <div class="modal-body">
                    <img src="" id="preview-image" class="img-responsive">
                </div>
                <div class="modal-footer">
                    <button class="btn btn-default" type="button" data-dismiss="modal">Cancel</button>
                </div>
            </div>
        </div>
    </div>

    紧接着在 admin/upload/index.blade.php 尾部写js:

    @section('scripts')
        <script>
            // 删除文件
            function delete_file(name){
                $("#delete-file-name1").html(name);
                $("#modal-file-delete").modal("show");
            }
    
            // 删除文件夹
            function delete_folder(name){
                $("#delete-folder-name1").html(name);
                $("#delete-folder-name2").val(name);
                $("#modal-folder-delete").modal("show");
            }
    
            // 上传图片
            function preview_image(path) {
                $("#preview-image").attr("src", path);
                $("#modal-image-view").modal("show");
            }
            $(function(){
                $("#uploads-table").DataTable();
            })
        </script>
    @endsection

    至此,咱就可以浏览到效果了。


    4 实现功能

    4.1 准备

    首先添加路由:

    Route::group(['namespace' => 'Admin', 'middleware' => 'auth', 'prefix' => 'admin'], function(){
        Route::resource('post', 'PostController');
        Route::resource('tag', 'TagController', ['except' => 'show']);
    
        Route::get('upload', 'UploadController@index');
        // 上传文件
        Route::post('upload/file', 'UploadController@uploadFile');
        // 删除文件
        Route::delete('upload/file', 'UploadController@deleteFile');
        // 创建文件夹
        Route::post('upload/folder', 'UploadController@createFolder');
        // 删除文件夹
        Route::delete('upload/folder', 'UploadController@deleteFolder');
    });

    创建需要的Request来进行表单认证:

    class UploadFileRequest extends Request
    {
        /**
         * Determine if the user is authorized to make this request.
         *
         * @return bool
         */
        public function authorize()
        {
            return true;
        }
    
        /**
         * Get the validation rules that apply to the request.
         *
         * @return array
         */
        public function rules()
        {
            return [
                // 文件必须存在
                'file' => 'required',
                // 路径必须存在 以保证上传文件的路径
                'folder' => 'required'
            ];
        }
    }
    <?php
    
    namespace AppHttpRequests;
    
    use AppHttpRequestsRequest;
    
    class UploadNewFolderRequest extends Request
    {
        /**
         * Determine if the user is authorized to make this request.
         *
         * @return bool
         */
        public function authorize()
        {
            return true;
        }
    
        /**
         * Get the validation rules that apply to the request.
         *
         * @return array
         */
        public function rules()
        {
            return [
                // 要创建目录所需要的目录必须存在
                'folder' => 'required',
                // 新创建的目录
                'new_folder' => 'required'
            ];
        }
    }

    4.2 创建新的目录

    UploadControllercreateFolder方法:

        public function createFolder(RequestsUploadNewFolderRequest $request)
        {
            // 取到我们要创建的目录
            $new_folder = $request->get('new_folder');
            // 拼接上当前的路径
            $folder = $request->get('folder') . '/' . $new_folder;
            // 创建新的目录
            $result = $this->manager->createDirectory($folder);
    
            if ($result === true){
                return redirect()->back()->withSuccess("Folder $new_folder created.");
            }
            $error = $result ?  : "An error occurred creating directory";
            return redirect()->back()->withErrors([$error]);
        }

    uploadManager下完善刚刚用到的方法createDirectory

        /**
         * 创建一个目录 成功时返回true错误时返回错误信息或false。
         *
         * @param $folder
         * @return string|bool
         */
        public function createDirectory($folder)
        {
            $folder = $this->cleanFolder($folder);
            // 判断是否存在
            if ($this->disk->exists($folder)){
                return "Folder $folder already exists";
            }
            return $this->disk->makeDirectory($folder);
        }

    4.3 删除目录

        public function deleteFolder(Request $request)
        {
            // 获取要删除的目录
            $del_folder = $request->get('del_folder');
            // 拼接完整路径
            $folder = $request->get('folder') . '/' . $del_folder;
            $result = $this->manager->deleteDirectory($folder);
    
            if ($result === true){
                return redirect()->back()->withSuccess("Folder $del_folder deleted.");
            }
            return redirect()->back()->withErrors($result);
        }

    uploadManager下完善刚刚用到的方法deleteDirectory

        /**
         * 删除一个目录 成功时返回true错误时返回错误信息或false。
         *
         * @param $folder
         * @return string|bool
         */
        public function deleteDirectory($folder)
        {
            $folder = $this->cleanFolder($folder);
            // 获取目录下的所有子目录和文件
            $filesFolders = array_merge(
                $this->disk->directories($folder),
                $this->disk->files($folder)
            );
            // 判断数组是否是空的
            if (!empty($filesFolders)){
                // 如果不是空的 则不能删除
                return "Directory must be empty to delete it.";
            }
            return $this->disk->deleteDirectory($folder);
        }

    4.4 删除文件

        public function deleteFile(Request $request)
        {
            $del_file = $request->get('del_file');
            $path = $request->get('folder').'/'.$del_file;
    
            $result = $this->manager->deleteFile($path);
    
            if ($result === true) {
                return redirect()
                    ->back()
                    ->withSuccess("File '$del_file' deleted.");
            }
    
            $error = $result ? : "An error occurred deleting file.";
            return redirect()
                ->back()
                ->withErrors([$error]);
        }

    uploadManager下完善刚刚用到的方法deleteDirectory

        /**
         * 删除一个文件 成功时返回true错误时返回错误信息或false。
         * 
         * @param $path
         * @return string|bool
         */
        public function deleteFile($path)
        {
            $path = $this->cleanFolder($path);
    
            // 判断文件是否存在
            if (! $this->disk->exists($path)) {
                return "File does not exist.";
            }
    
            return $this->disk->delete($path);
        }

    4.5 上传文件

        public function uploadFile(RequestsUploadFileRequest $request)
        {
            // 获取到文件信息
            $file = $_FILES['file'];
            // 获取文件名
            $fileName = $request->get('file_name');
            $fileName = $fileName ?: $file['name'];
            // 拼接路径
            $path = str_finish($request->get('folder'), '/') . $fileName;
            // 获取内容
            $content = File::get($file['tmp_name']);
    
            $result = $this->manager->saveFile($path, $content);
    
            if ($result === true) {
                return redirect()
                    ->back()
                    ->withSuccess("File '$fileName' uploaded.");
            }
    
            $error = $result ? : "An error occurred uploading file.";
            return redirect()
                ->back()
                ->withErrors([$error]);
        }

    uploadManager下完善刚刚用到的方法deleteDirectory

        /**
         * 保存文件 成功时返回true错误时返回错误信息或false。
         *
         * @param $path
         * @param $content
         * @return string
         */
        public function saveFile($path, $content)
        {
            $path = $this->cleanFolder($path);
    
            if ($this->disk->exists($path)) {
                return "File already exists.";
            }
    
            return $this->disk->put($path, $content);
        }

    注意:如果重命名文件名的话要自行加上后缀哦

  • 相关阅读:
    Jenkins的maven工程打包的时候怎么指定不同环境的配置文件
    杂记待整理
    zabbix监控网络的出入口流量
    MySQL的恢复脚本
    mysql 备份恢复图
    切割haproxy的日志
    zabbix 监控MySQL
    svn 的备份还原
    svn做目录访问控制(AuthzSVNAccessFile)
    MySQL多实例
  • 原文地址:https://www.cnblogs.com/sun-kang/p/7643595.html
Copyright © 2020-2023  润新知