图片/文件上传
<h1>图片/文件上传</h1>
<p><a href="model-form.md">数据表单</a>通过以下的调用来生成图片/文件上传表单,支持本地和云存储的文件上传。上传组件是基于<a href="https://fex.baidu.com/webuploader/">webuploader</a>实现的,具体的使用配置可参考<a href="https://fex.baidu.com/webuploader/document.html">webuploader官方文档</a>。</p>
<p>> {tip} 文件或图片上传表单字段请不要在模型中设置<strong>访问器</strong>和<strong>修改器</strong>拼接域名,如有相关需求可参考<a href="#withhost">文件/图片域名拼接</a>。</p>
<pre><code class="language-php">$form-&gt;file('file_column');
$form-&gt;image('image_column');</code></pre>
<p><a name="local"></a></p>
<h2>本地上传</h2>
<p>先添加存储配置,<code>config/filesystems.php</code> 添加一项<code>disk</code>:</p>
<pre><code class="language-php">
'disks' =&gt; [
... ,
'admin' =&gt; [
'driver' =&gt; 'local',
'root' =&gt; public_path('uploads'),
'visibility' =&gt; 'public',
'url' =&gt; env('APP_URL').'/uploads',
],
],
</code></pre>
<p>设置上传的路径为<code>public/uploads</code>(public_path('uploads'))。</p>
<p>然后选择上传的<code>disk</code>,打开<code>config/admin.php</code>找到:</p>
<pre><code class="language-php">
'upload' =&gt; [
'disk' =&gt; 'admin',
'directory' =&gt; [
'image' =&gt; 'images',
'file' =&gt; 'files',
]
],
</code></pre>
<p>将<code>disk</code>设置为上面添加的<code>admin</code>,<code>directory.image</code>和<code>directory.file</code>分别为用<code>$form-&gt;image($column)</code>和<code>$form-&gt;file($column)</code>上传的图片和文件的上传目录。</p>
<p>当然你也可以在代码中指定<code>disk</code>:</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;disk('your disk name');</code></pre>
<p><a name="oss"></a></p>
<h2>云盘上传</h2>
<p>如果需要上传到云存储,需要安装对应<code>laravel storage</code>的适配器,拿七牛云存储举例</p>
<p>首先安装 <a href="https://github.com/zgldh/qiniu-laravel-storage">zgldh/qiniu-laravel-storage</a></p>
<p>同样配置好disk,在<code>config/filesystems.php</code> 添加一项:</p>
<pre><code class="language-php">'disks' =&gt; [
... ,
'qiniu' =&gt; [
'driver' =&gt; 'qiniu',
'domains' =&gt; [
'default' =&gt; 'xxxxx.com1.z0.glb.clouddn.com', //你的七牛域名
'https' =&gt; 'dn-yourdomain.qbox.me', //你的HTTPS域名
'custom' =&gt; 'static.abc.com', //你的自定义域名
],
'access_key'=&gt; '', //AccessKey
'secret_key'=&gt; '', //SecretKey
'bucket' =&gt; '', //Bucket名字
'notify_url'=&gt; '', //持久化处理回调地址
'url' =&gt; 'http://of8kfibjo.bkt.clouddn.com/', // 填写文件访问根url
],
],
</code></pre>
<p>然后修改<code>dcat-admin</code>的上传配置,打开<code>config/admin.php</code>找到:</p>
<pre><code class="language-php">
'upload' =&gt; [
'disk' =&gt; 'qiniu',
'directory' =&gt; [
'image' =&gt; 'image',
'file' =&gt; 'file',
],
],
</code></pre>
<p><code>disk</code>选择上面配置的<code>qiniu</code>,或:</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;disk('qiniu');</code></pre>
<p><a name="public"></a></p>
<h2>公共方法</h2>
<h3>缩略图 (thumbnail)</h3>
<p>上传图片的同时生成缩略图</p>
<pre><code class="language-php">$form-&gt;image($column[, $label])-&gt;thumbnail('small', $width = 300, $height = 300);
// 生成多张缩略图
$form-&gt;image($column[, $label])-&gt;thumbnail([
'small1' =&gt; [100, 100],
'small2' =&gt; [200, 200],
'small3' =&gt; [300, 300],
]);</code></pre>
<pre><code class="language-php">use Dcat\Admin\Traits\Resizable;
class Photo extends Model
{
use Resizable;
}
// To access thumbnail
$photo-&gt;thumbnail('small', 'photo_column');</code></pre>
<p><a name="disk"></a></p>
<h3>存储驱动 (disk)</h3>
<p>修改文件上传源</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;disk('your disk name');</code></pre>
<p><a name="move"></a></p>
<h3>上传路径 (move)</h3>
<p>修改上传路径</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;move('public/upload/image1/');</code></pre>
<p><a name="name"></a></p>
<h3>文件名称 (name)</h3>
<p>修改上传文件名称</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;name('test.text');
$form-&gt;image('picture')-&gt;name(function ($file) {
return 'test.'.$file-&gt;guessExtension();
});</code></pre>
<p><a name="uniqueName"></a></p>
<h3>随机名称 (uniqueName)</h3>
<p>使用随机生成文件名 (md5(uniqid()).extension)</p>
<pre><code class="language-php">$form-&gt;image('picture')-&gt;uniqueName();</code></pre>
<p><a name="removable"></a></p>
<h3>禁止页面删除文件 (替换上传)</h3>
<p>通过<code>removable</code>方法可以禁止用户从页面点击删除服务器上的文件,可以实现图片覆盖上传效果。</p>
<pre><code class="language-php">$form-&gt;file($column[, $label])-&gt;removable(false);</code></pre>
<p><a name="autoUpload"></a></p>
<h3>自动上传 (autoUpload)</h3>
<p>开启这个功能之后选择完文件之后会立即自动上传,页面将不再显示<code>上传</code>按钮,使用方法如下</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;autoUpload();
$form-&gt;image('img')-&gt;autoUpload();</code></pre>
<p><a name="retainable"></a></p>
<h3>禁止删除 (retainable)</h3>
<p>开启这个功能之后文件将不会从服务器删除</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;retainable();
$form-&gt;image('img')-&gt;retainable();</code></pre>
<p><a name="storagePermission"></a></p>
<h3>storagePermission</h3>
<p>设置上传文件的权限</p>
<pre><code class="language-php">$form-&gt;image('picture')-&gt;storagePermission(777);</code></pre>
<p><a name="accept"></a></p>
<h3>限制上传文件类型</h3>
<p>限制上传文件的类型</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;accept('jpg,png,gif,jpeg');
// 可以指定 mimeTypes, 多个用逗号分割
$form-&gt;file('file')-&gt;accept('jpg,png,gif,jpeg', 'image/*');</code></pre>
<p><a name="chunked"></a></p>
<h3>分块上传 (chunked)</h3>
<p>启用分块上传</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;chunked();</code></pre>
<p><a name="chunkSize"></a></p>
<h3>分块大小(chunkSize)</h3>
<p>设置分块大小,单位为<code>KB</code>,默认<code>5MB</code></p>
<p>> {tip} 调用这个方法会自动启用分块上传</p>
<pre><code class="language-php">// 设置为 1MB
$form-&gt;file('file')-&gt;chunkSize(1024);</code></pre>
<p><a name="maxSize"></a></p>
<h3>文件大小(maxSize)</h3>
<p>设置单个文件最大大小,单位为<code>Kb</code>,默认大小为<code>10M</code>。</p>
<p>> {tip} 同时应该保证<code>php.ini</code>配置文件的<code>upload_max_filesize</code>参数值必须大于这个方法设置的值。</p>
<pre><code class="language-php">// 设置单个文件最大为1Mb
$form-&gt;file('file')-&gt;maxSize(1024);</code></pre>
<p><a name="threads"></a></p>
<h3>并发上传线程数 (threads)</h3>
<p>设置并发上传线程数,默认<code>3</code></p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;threads(5);</code></pre>
<p><a name="url"></a></p>
<h3>自定义上传接口 (url)</h3>
<p>通过<code>url</code>可以设置自定义上传接口</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;url('users/files');</code></pre>
<p>系统提供了<code>Dcat\Admin\Traits\HasUploadedFile</code>这个<code>trait</code>来帮助开发者更轻松地处理上传文件,用法如下</p>
<pre><code class="language-php">&lt;?php
namespace App\Admin\Controllers;
use Dcat\Admin\Traits\HasUploadedFile;
class FileController
{
use HasUploadedFile;
public function handle()
{
$disk = $this-&gt;disk('local');
// 判断是否是删除文件请求
if ($this-&gt;isDeleteRequest()) {
// 删除文件并响应
return $this-&gt;deleteFileAndResponse($disk);
}
// 获取上传的文件
$file = $this-&gt;file();
// 获取上传的字段名称
$column = $this-&gt;uploader()-&gt;upload_column;
$dir = 'my-images';
$newName = $column.'-我的文件名称.'.$file-&gt;getClientOriginalExtension();
$result = $disk-&gt;putFileAs($dir, $file, $newName);
$path = &quot;{$dir}/$newName&quot;;
return $result
? $this-&gt;responseUploaded($path, $disk-&gt;url($path))
: $this-&gt;responseErrorMessage('文件上传失败');
}
}</code></pre>
<p>在你的路由文件<code>app\Admin\routes.php</code>中加上</p>
<pre><code class="language-php">$router-&gt;any('users/files', 'FileController@handle');</code></pre>
<p><a name="deleteUrl"></a></p>
<h3>deleteUrl</h3>
<p>修改删除已上传文件路径,此方法一般不需要修改</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;deleteUrl('file/delete');</code></pre>
<p><a name="autoSave"></a></p>
<h3>自动保存字段值 (autoSave)</h3>
<p>设置上传文件后是否自动保存文件路径到数据库,此方法默认启用,一般不需要修改</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;autoSave(false);</code></pre>
<p><a name="options"></a></p>
<h3>配置 (options)</h3>
<p>自定义<a href="https://fex.baidu.com/webuploader/document.html">webuploader</a>配置</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;options(['disableGlobalDnd' =&gt; true]);</code></pre>
<h3>可排序 (sortable)</h3>
<p>此方法仅针对多图/文件上传表单有效</p>
<pre><code class="language-php">$form-&gt;multipleImage('images')-&gt;sortable();</code></pre>
<h3>压缩图片 (compress)</h3>
<p>默认不启用</p>
<pre><code class="language-php">// 启用图片压缩功能
$form-&gt;multipleImage('images')-&gt;compress();
$form-&gt;image('avatar')-&gt;compress([
'width' =&gt; 1600,
'height' =&gt; 1600,
// 图片质量,只有type为`image/jpeg`的时候才有效。
'quality' =&gt; 90,
// 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false.
'allowMagnify' =&gt; false,
// 是否允许裁剪。
'crop' =&gt; false,
// 是否保留头部meta信息。
'preserveHeaders' =&gt; true,
// 如果发现压缩后文件大小比原来还大,则使用原来图片
// 此属性可能会影响图片自动纠正功能
'noCompressIfLarger' =&gt; false,
// 单位字节,如果图片大小小于此值,不会采用压缩。
'compressSize' =&gt; 0
]);</code></pre>
<h3>监听WebUploader文件上传事件 (on)</h3>
<p>通过 <code>on</code> 方法可以监听 <a href="http://fex.baidu.com/webuploader/doc/index.html#WebUploader_Uploader_events">WebUploader文件上传相关事件</a></p>
<pre><code class="language-php">$form-&gt;file('...')
-&gt;on('startUpload', &lt;&lt;&lt;JS
function () {
console.log('文件开始上传...', this);
// 上传文件前附加自定义参数到文件上传接口
this.uploader.options.formData['custom_field'] = '...';
}
&lt;&lt;&lt;JS
)
-&gt;on('uploadFinished', &lt;&lt;&lt;JS
function () {
console.log('文件上传完毕');
}
&lt;&lt;&lt;JS
);</code></pre>
<p><a name="withhost"></a></p>
<h3>文件/图片域名拼接</h3>
<p>文件或图片上传表单字段请不要在模型中设置<strong>访问器</strong>和<strong>修改器</strong>拼接域名,如果你需要在访问的时候拼接完整域名,可以在模型中定义一个<code>public</code>方法</p>
<pre><code class="language-php">&lt;?php
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;
class YourModel extends Model
{
// 定义一个public方法访问图片或文件
public function getImage()
{
if (Str::contains($this-&gt;image, '//')) {
return $this-&gt;image;
}
return Storage::disk('admin')-&gt;url($this-&gt;image);
}
}</code></pre>
<h3>保存域名</h3>
<p>如果你需要保存文件域名到数据表,可以使用<code>saveFullUrl</code>方法</p>
<pre><code class="language-php">$form-&gt;image('avatar')-&gt;saveFullUrl();
$form-&gt;file('...')-&gt;saveFullUrl();</code></pre>
<h3>监听文件上传变动 (change)</h3>
<p>通过以下方法可以监听文件<strong>上传成功</strong>或文件<strong>被删除</strong>时产生的变动</p>
<pre><code class="language-php">$file = $form-&gt;file('...');
Admin::script(
&lt;&lt;&lt;JS
$('{$file-&gt;getElementClassSelector()} .file-input').on('change', function () {
console.log('文件发生变动', this.value);
});
JS
);</code></pre>
<p><a name="override"></a></p>
<h3>覆盖上传 (override)</h3>
<p>通过 <code>override</code> 方法可以实现文件覆盖上传。</p>
<pre><code class="language-php">$form-&gt;file('file')-&gt;override();
$form-&gt;image('img')-&gt;override();</code></pre>
<p><a name="imagefun"></a></p>
<h2>图片上传内置方法</h2>
<p><a name="intervention"></a></p>
<h3>压缩、裁切、添加水印等</h3>
<p>可以使用压缩、裁切、添加水印等各种方法,需要先安装<a href="http://image.intervention.io/getting_started/installation">intervention/image</a>.</p>
<p>更多使用方法请参考[<a href="http://image.intervention.io/getting_started/introduction">Intervention</a>]:</p>
<pre><code class="language-php">$form-&gt;image($column[, $label]);
// 修改图片上传路径和文件名
$form-&gt;image($column[, $label])-&gt;move($dir, $name);
// 剪裁图片
$form-&gt;image($column[, $label])-&gt;crop(int $width, int $height, [int $x, int $y]);
// 加水印
$form-&gt;image($column[, $label])-&gt;insert($watermark, 'center');</code></pre>
<p><a name="dimensions"></a></p>
<h3>限制上传图片的尺寸</h3>
<p>设置文件上传尺寸限制</p>
<p>参数: <code>array</code> 单位为像素</p>
<ul>
<li><code>width</code> 指定宽度</li>
<li><code>height</code> 指定高度</li>
<li><code>min_width</code> 最小宽度</li>
<li><code>min_height</code> 最小高度</li>
<li><code>max_width</code> 最大宽度</li>
<li><code>max_height</code> 最大高度</li>
<li><code>ratio</code> 宽高比 (width/height)</li>
</ul>
<pre><code class="language-php">// 上传宽度为100-300像素之间的图片
$form-&gt;image('img')-&gt;dimensions(['min_width' = 100, 'max_width' =&gt; 300]);</code></pre>
<p><a name="referer"></a></p>
<h2>把图片/文件路径保存在其他数据表</h2>
<p>通过下面的方法可以把图片或文件的路径保存在单独的附件表,而当前的图片/文件字段只保存ID</p>
<p>> {tip} 这里的示例用的是单图上传表单,如果是多图的话可以按这个思路自行调整。</p>
<p>图片/文件表结构</p>
<pre><code class="language-sql">CREATE TABLE `images` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`path` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;</code></pre>
<p>使用</p>
<pre><code class="language-php">$form-&gt;image('image1')
-&gt;saving(function ($value) use ($form) {
if ($form-&gt;isEditing() &amp;&amp; ! $value) {
// 编辑页面,删除图片逻辑
Image::destroy($form-&gt;model()-&gt;image1);
return;
}
// 新增或编辑页面上传图片
if ($value) {
$model = Image::where('path', $value)-&gt;first();
}
if (empty($model)) {
$model = new Image();
}
$model-&gt;path = $value;
$model-&gt;save();
return $model-&gt;getKey();
})
-&gt;customFormat(function ($v) {
if (! $v) {
return;
}
return Image::find((array) $v)-&gt;pluck('path')-&gt;toArray();
});</code></pre>
<h2>文件上传失败或无法访问?</h2>
<p>如果你发现无法上传文件,那么通常有几下几点原因:</p>
<ol>
<li><code>Laravel</code>文件上传配置不正确,请检查<code>admin.upload.disk</code>参数。如果你不了解<code>laravel</code>文件上传功能,请阅读文档<a href="https://learnku.com/docs/laravel/7.x/filesystem/7485">Laravel - 文件存储</a></li>
<li>文件过大,需要调整<code>php.ini</code>的<code>upload_max_filesize</code>参数</li>
<li>文件上传目录没有写权限</li>
<li><code>php</code>没有安装或没有开启<code>fileinfo</code>扩展</li>
</ol>
<p>如果你的文件上传成功了,却无法正常访问,那么可能是<code>.env</code>配置文件中的<code>APP_URL</code>参数没有设置正确。</p>