教程源自:Laravel學院php
這一節 咱來講說上傳文件的功能實現,咱們會把上傳的文件保存到項目本地,不只上傳 還有刪除和預覽功能。html
咱們先從配置開始作起,先修改咱們本身建立的 blog.phplaravel
<?php return [ 'title' => "Larger K's Blog", 'posts_per_page' => 10, 'uploads' => [ 'storage' => 'local', // 儲存處 'webpath' => '/uploads' // 儲存路徑 ], ];
而後編輯 config/filesystems.phpweb
'disks' => [ 'local' => [ 'driver' => 'local', // 'root' => storage_path('app'), 'root' => public_path('uploads'), ],
咱們須要建立一個Manager來封裝須要用到的功能。apache
difydev包是主要用於分別MIME類型的,咱們須要根據不一樣的文件類型進行不一樣的操做,因此須要使用到它,若是你記不住它的名字能夠到Packagist 中搜索 MIMEjson
而後咱們使用 composer 引入它:數組
composer require "dflydev/apache-mime-types"
咱們在 app/Service 目錄下建立:app
<?php namespace App\Services; use Carbon\Carbon; use Dflydev\ApacheMimeTypes\PhpRepository; use Illuminate\Support\Facades\Storage; use phpDocumentor\Reflection\DocBlock\Tags\Return_; 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這個方法 它返回咱們須要用到的一些數據composer
其實把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.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
至此,咱就能夠瀏覽到效果了。
首先添加路由:
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 App\Http\Requests; use App\Http\Requests\Request; 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' ]; } }
UploadController的createFolder方法:
public function createFolder(Requests\UploadNewFolderRequest $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); }
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); }
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); }
public function uploadFile(Requests\UploadFileRequest $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); }
注意:若是重命名文件名的話要自行加上後綴哦