教程源自: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 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这个方法 它返回我们需要用到的一些数据

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 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'
];
}
}

4.2 创建新的目录

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);
}

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(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);
}

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

Laravel5.1 搭建博客 --上传文件及文件管理的更多相关文章

  1. Laravel5.1 搭建博客 --编译前端文件

    上篇文章写了Gulp编译前端文件,这篇记录下在搭建博客中使用Gulp 1 引入bootstrap和js 1.1 首先先在项目本地安装Bower sudo npm install bower 1.2 创 ...

  2. wordpress搭建博客上传begin主题The themes is locked to another domain

    如题, 在使用wordpress搭建个人博客过程中, 上传begin主题, 出现如下弹框的错误, 而且样式有误. 环境: Los Angeles CentOS 7 x64 nginx+mysql 解决 ...

  3. hexo搭建博客上传至github

    准备工作 ssh key 要先申请好,并且添加至github的ssh库中 安装插件 hexo-deployer-git npm install hexo-deployer-git --save 配置 ...

  4. Day12-微信小程序实战-交友小程序-搭建服务器与上传文件到后端

    要搞一个小型的cms内容发布系统 因为小程序上线之后,直接对数据库进行操作的话,慧出问题的,所以一般都会做一个管理系统,让工作人员通过这个管理系统来对这个数据库进行增删改查 微信小程序其实给我们提供了 ...

  5. Laravel5.1 搭建博客 --展示简单的首页

    今天起开始搭建博客,把之前学的东西运用下. 1 创建 配置项目 1.1 创建项目 composer create-project laravel/laravel blog 5.1.1 1.2 配置数据 ...

  6. Laravel5.1 搭建博客 --后台登录

    今天咱来实现后台的登录. 首先我们的后台需要三个控制器: PostController:管理文章. TagController:管理文章标签. UploadController:上传文件. 当我们访问 ...

  7. csdn的博客上传word图片

    目前大部分的博客作者在用Word写博客这件事情上都会遇到以下3个痛点: 1.所有博客平台关闭了文档发布接口,用户无法使用Word,Windows Live Writer等工具来发布博客.使用Word写 ...

  8. Laravel5.1 搭建博客 --构建标签

    博客的每篇文章都是需要有标签的,它与文章也是多对多的关系 这篇笔记也是记录了实现标签的步骤逻辑. 在我们之前的笔记中创建了Tag的控制器和路由了 所以这篇笔记不在重复 1 创建模型与迁移文件 迁移文件 ...

  9. Laravel5.1 搭建博客 --文章的增删改查

    教程源于:Laravel学院 继文件上传后呢,咱来搞一搞文章的事情. 1 更改数据表 我们需要改改数据表的结构 因为涉及到重命名列名 所以咱需要引入一个包:Doctrine: composer req ...

随机推荐

  1. Android -BLE蓝牙小DEMO

    代码地址如下:http://www.demodashi.com/demo/13890.html 原文地址: https://blog.csdn.net/vnanyesheshou/article/de ...

  2. bash算术求值和errexit陷阱

    原文:https://www.technovelty.org//linux/bash-arithmetic-evaluation-and-errexit-trap.html 在 "traps ...

  3. 【centos6.5】安装LAMP

    转载至:linux公社  https://www.linuxidc.com/Linux/2014-07/104563.htm

  4. MySQL-锁研究

    隔离级别研究: http://www.cnblogs.com/JohnABC/p/3521061.html 表级:引擎 MyISAM, 理解为锁住整个表, 锁定期间, 其它进程无法对该表进行写操作, ...

  5. MSSQL排序规则冲突

    更改表字段排序规则 ALTER TABLE 表名 ALTER COLUMN 字段名 数据类型(长度) COLLATE 排列规则

  6. scrapy添加 请求头

    直接在 setting 文件中添加

  7. 浅谈AngularJS的$parse服务

    $parse 作用:将一个AngularJS表达式转换成一个函数 Usage$parse(expression) arguments expression:需要被编译的AngularJS语句 retu ...

  8. ios端 返回上一级后 卡在正在加载中处理方式

    //返回上一页 $('#goback').click(function () { history.back(); }); //判断是否为ios系统,若为ios则监听并重载 var u = naviga ...

  9. Eclipse 最有用的快捷键

    Eclipse中10个最有用的快捷键组合  一个Eclipse骨灰级开发者总结了他认为最有用但又不太为人所知的快捷键组合.通过这些组合可以更加容易的浏览源代码,使得整体的开发效率和质量得到提升.    ...

  10. echarts 百度图表

    手册说明 http://echarts.baidu.com/option.html 3.0版本加了很多新属性可以看以上链接  此文用的3.x版本 第一步 引入 <script src=" ...