乐趣区

关于laravel:dcatadmin-大文件上传前端直传解决

昨天遇到了一个后端传输导致 nginx502 的问题

明天在调整了代码之后,遂将解决方案贴出

前端直传的益处: 加重应用服务器的压力,将压力分给了 oss 这一点特地是在上传大文件时特地显著的,php 是要耗费很大一部分内存去解决前端分片上传来的文件再传输给 oss,如果文件特地大,耗时长 nginx 会间接502 咱们没必要去调整 nginx 的超时工夫把路走窄了。间接由客户端直传 oss 吧。

实现后效果图

因为 dcat-admin 是高度封装的。改它的组件根本不事实,不过 $form->view() 办法能够引入一个视图文件. 我的想法是用 vue 封装一个上传的组件,而后通过该办法引入。正好 laravel 提供了前端脚手架 laravel mix 整合了 vue。

  • laravel 版本 7.x

#### 开始一套梭

composer require laravel/ui --dev // 装置前端脚手架
php artisan ui vue // 公布 vue 文件
npm install
npm install ali-oss --save // 装置 oss js-sdk
npm install clipboard --save // 装置复制插件
npm run watch  // 命令监督热加载、编译

应用 element-ui 的组件, 所以咱们引入它

npm i element-ui -S

app.js 文件全局加载

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
import Clipboard from 'clipboard';
Vue.prototype.Clipboard=Clipboard;

并新建组件OssFile

<template>
    <div>
        <el-upload
            class="upload-demo"
            action=""ref="upload":file-list="fileList":limit="2":on-change="handleChange":on-remove="handleRemove":auto-upload="false"accept=""
        >
            <el-button slot="trigger" size="small" type="primary"> 选取文件 </el-button>
            <el-button style="margin-left: 10px;" size="small" type="success" @click="submitForm"> 直传 oss</el-button>
            <el-button style="margin-left: 10px;" size="small" type="success" @click="resumeUpload"> 持续 </el-button>
            <el-button style="margin-left: 10px;" size="small" type="success" @click="stopUplosd"> 暂停 </el-button>
            <el-button style="margin-left: 10px;" size="small" type="success" @click="abortMultipartUpload"> 革除切片 </el-button>
        </el-upload>
        <el-progress :percentage="percentage" :status="uploadStatus"></el-progress>
        <span
            class="copybtn"
            @click="copy"
            :data-clipboard-text="fileName"
        >
{{fileName}}
</span>
    </div>
</template>

<script>

import Clipboard from 'clipboard';
let OSS = require('ali-oss') // 引入 ali-oss 插件
const client = new OSS({
    region: 'oss-cn-shenzhen',// 依据那你的 Bucket 地点来填写
    accessKeyId: '',// 本人账户的 accessKeyId
    accessKeySecret: '',// 本人账户的 accessKeySecret
    bucket: '',//bucket 名字
});
export default {
    name: "OssFile",
    data () {
        return {
            fileName:"",
            fileList:[],
            file: null,
            tempCheckpoint: null, // 用来缓存以后切片内容
            uploadId: '',
            uploadStatus: null, // 进度条上传状态
            percentage: 0, // 进度条百分比
            uploadName: '',  //Object 所在 Bucket 的残缺门路
        }
    },
    mounted() {window.addEventListener('online',  this.resumeUpload);
    },
    methods: {copy()
        {var clipboard = new Clipboard(".copybtn");
            clipboard.on("success", (e) => {
                this.$message({
                    message: '复制胜利',
                    type: 'success'
                });
                // 开释内存
                clipboard.destroy();});
            clipboard.on("error", (e) => {
                // 不反对复制
                this.$message({
                    message: '该浏览器不反对主动复制',
                    type: 'success'
                });
                // 开释内存
                clipboard.destroy();});
        },
        // 点击上传至服务器
        submitForm(file) {this.multipartUpload();
        },
        // 勾销分片上传事件
        async abortMultipartUpload() {window.removeEventListener('online', this.resumeUpload)
            const name = this.uploadName; // Object 所在 Bucket 的残缺门路。const uploadId = this.upload; // 分片上传 uploadId。const result = await client.abortMultipartUpload(name, uploadId);
            console.log(result, '======= 革除切片 ====');
        },
        // 暂停分片上传。stopUplosd () {window.removeEventListener('online', this.resumeUpload) // 暂停时革除工夫监听
            let result = client.cancel();
            console.log(result, '--------- 暂停上传 -----------')
        },
        // 切片上传
        async multipartUpload () {if (!this.file) {this.$message.error('请抉择文件')
                return
            }

            console.log("this.uploadStatus",this.file, this.uploadStatus);
            console.log("文件列表:"+this.fileList)
            console.log("文件:"+this.file)
            this.percentage = 0
            try {
                //object-name 能够自定义为文件名(例如 file.txt)或目录(例如 abc/test/file.txt)的模式,实现将文件上传至以后 Bucket 或 Bucket 下的指定目录。let result = await client.multipartUpload(this.file.name, this.file, {
                    headers: {
                        'Content-Disposition': 'inline',
                        'Content-Type': this.file.type // 留神:依据图片或者文件的后缀来设置,我试验用的‘.png’的图片,具体为什么下文解释
                    },
                    progress: (p, checkpoint) => {
                        this.tempCheckpoint = checkpoint;
                        this.upload = checkpoint.uploadId
                        this.uploadName = checkpoint.name
                        this.percentage = p * 100
                        // console.log(p, checkpoint, this.percentage, '---------uploadId-----------')
                        // 断点记录点。浏览器重启后无奈间接持续上传,您须要手动触发上传操作。},
                    meta: {year: 2020, people: 'dev'},
                    mime: this.file.type
                });
                console.log(result, this.percentage, 'result= 切片上传完毕 =');


                this.$nextTick(()=>{this.fileName = 'https://image.mythinkcar.cn/'+result.name})
                console.log(this.fileName)

            } catch (e) {console.log(e)
                window.addEventListener('online',  this.resumeUpload) // 该监听放在断网的异样解决
                // 捕捉超时异样。if (e.code === 'ConnectionTimeoutError') { // 申请超时异样解决
                    this.uploadStatus = 'exception'
                    console.log("TimeoutError");
                }

            }
        },
        // 复原上传。async resumeUpload () {window.removeEventListener('online', this.resumeUpload)
            if (!this.tempCheckpoint) {this.$message.error('请先上传')
                return
            }
            this.uploadStatus = null
            try {
                let result = await client.multipartUpload(this.file.name, this.file, {
                    headers: {
                        'Content-Disposition': 'inline',
                        'Content-Type': this.file.type // 留神:依据图片或者文件的后缀来设置,我试验用的‘.png’的图片,具体为什么下文解释
                    },

                    progress: (p, checkpoint) => {
                        this.percentage = p * 100
                        console.log(p, checkpoint, 'checkpoint---- 复原上传的切片信息 -------')
                        this.tempCheckpoint = checkpoint;
                    },
                    checkpoint: this.tempCheckpoint,
                    meta: {year: 2020, people: 'dev'},
                    mime: this.file.type
                })
                console.log(result, 'result-=-=- 复原上传完毕')
            } catch (e) {console.log(e, 'e-=-=-');
            }
        },

        // 抉择文件产生扭转
        handleChange(file, fileList) {this.fileList = fileList.filter(row => row.uid == file.uid)
            this.file = file.raw
            // 文件扭转时上传
            // this.submitForm(file)
        },
        handleRemove(file, fileList) {
            this.percentage = 0 // 进度条置空
            this.fileList = []},
    }
}
</script>


<style>
.avatar-uploader .el-upload {
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
}
.avatar-uploader .el-upload:hover {border-color: #409EFF;}
.avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 150px;
    height: 150px;
    line-height: 150px;
    text-align: center;
}
.avatar {
    width: 150px;
    height: 150px;
    display: block;
}
</style>

app.js中引入组件

Vue.component('oss-file', require('./components/uploads/OssFile.vue').default);

view目录新建oss.blade.php 引入组件 <oss-file></oss-file>

<link rel="stylesheet" href="{{mix('css/app.css')}}">
<div id="app">
<div class="container">
    <oss-file></oss-file>
</div>
</div>
<script src="{{mix('js/app.js')}}"></script>

最初

  $form->html(view('uploads.oss'));
  $form->text('link','直传后填入地址');

完满解决~~~

碰到的问题

  • oss 跨域问题
  • oss 呈现 RequestId 错误处理办法 新增 `ETag
    x-oss-request-id`

参考文章

  • oss 文档
    * 阿里 oss 呈现 RequestId 错误处理办法
退出移动版