|
|
@@ -0,0 +1,160 @@
|
|
|
+<template>
|
|
|
+ <div class="oss-upload-container">
|
|
|
+ <el-upload
|
|
|
+ action=""
|
|
|
+ :http-request="handleOssUpload"
|
|
|
+ :before-upload="beforeUpload"
|
|
|
+ :on-success="handleSuccess"
|
|
|
+ :on-error="handleError"
|
|
|
+ :show-file-list="true"
|
|
|
+ list-type="text"
|
|
|
+ >
|
|
|
+ <el-button size="small" type="primary">点击上传到 OSS</el-button>
|
|
|
+ <div slot="tip" class="el-upload__tip">只能上传不超过 100MB 的文件</div>
|
|
|
+ </el-upload>
|
|
|
+
|
|
|
+ <div v-if="signedUrl" class="video-preview-wrapper">
|
|
|
+ <p class="preview-title">视频上传成功,预览如下:</p>
|
|
|
+ <video
|
|
|
+ :src="signedUrl"
|
|
|
+ controls
|
|
|
+ autoplay
|
|
|
+ width="100%"
|
|
|
+ class="video-player"
|
|
|
+ >
|
|
|
+ 您的浏览器不支持 video 标签。
|
|
|
+ </video>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import axios from 'axios'
|
|
|
+import { addUploadedObject, getAliyunOssSignature } from '@/api/file'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'OssUpload',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ objectName: null,
|
|
|
+ signedUrl: null
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ /**
|
|
|
+ * 上传前的检查(可选)
|
|
|
+ */
|
|
|
+ beforeUpload(file) {
|
|
|
+ const isLt100M = file.size / 1024 / 1024 < 100
|
|
|
+ if (!isLt100M) {
|
|
|
+ this.$message.error('上传文件大小不能超过 100MB!')
|
|
|
+ }
|
|
|
+ return isLt100M
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 自定义 OSS 上传核心逻辑
|
|
|
+ */
|
|
|
+ async handleOssUpload(options) {
|
|
|
+ const { file, onProgress, onSuccess, onError } = options
|
|
|
+ try {
|
|
|
+ // 在开始上传新视频时,可以先清空上一个视频的链接,避免画面残留
|
|
|
+ this.signedUrl = null
|
|
|
+
|
|
|
+ // 1. 向你的后端服务器请求 OSS 签名数据
|
|
|
+ const res = await getAliyunOssSignature()
|
|
|
+ const ossConfig = res.data
|
|
|
+ console.log(ossConfig)
|
|
|
+
|
|
|
+ // 2. 构造唯一的文件名
|
|
|
+ const suffix = file.name.substring(file.name.lastIndexOf('.'))
|
|
|
+ const uniqueName = `${new Date().getTime()}_${Math.random().toString(36).substr(2, 5)}${suffix}`
|
|
|
+ const fileKey = 'a/b/' + uniqueName
|
|
|
+ this.objectName = fileKey
|
|
|
+
|
|
|
+ // 3. 组装符合 OSS 要求的 FormData
|
|
|
+ const formData = new FormData()
|
|
|
+ formData.append('key', fileKey)
|
|
|
+ formData.append('OSSAccessKeyId', ossConfig.OSSAccessKeyId)
|
|
|
+ formData.append('policy', ossConfig.policy)
|
|
|
+ formData.append('signature', ossConfig.signature)
|
|
|
+ formData.append('success_action_status', '200')
|
|
|
+ formData.append('file', file)
|
|
|
+
|
|
|
+ // 4. 使用 Axios 发起 POST 请求直传到 OSS
|
|
|
+ await axios.post(ossConfig.host, formData, {
|
|
|
+ headers: { 'Content-Type': 'multipart/form-data' },
|
|
|
+ onUploadProgress: (progressEvent) => {
|
|
|
+ if (progressEvent.total > 0) {
|
|
|
+ const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total)
|
|
|
+ onProgress({ percent })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 5. 拼接出文件的最终访问绝对路径
|
|
|
+ const fileUrl = `${ossConfig.host}/${fileKey}`
|
|
|
+
|
|
|
+ // 6. 调用 Element UI 成功的钩子
|
|
|
+ onSuccess({ url: fileUrl }, file)
|
|
|
+ } catch (err) {
|
|
|
+ console.error('OSS 上传失败:', err)
|
|
|
+ onError(err)
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上传成功回调
|
|
|
+ */
|
|
|
+ handleSuccess(response, file, fileList) {
|
|
|
+ this.$message.success('文件上传成功!')
|
|
|
+ const objectUrl = response.url
|
|
|
+ if (this.objectName !== null) {
|
|
|
+ const jsonData = {}
|
|
|
+ jsonData.sha256sum = ''
|
|
|
+ jsonData.objectName = this.objectName
|
|
|
+ jsonData.objectUrl = objectUrl
|
|
|
+ jsonData.filename = file.filename
|
|
|
+ jsonData.size = file.size
|
|
|
+ addUploadedObject(jsonData).then(resp => {
|
|
|
+ if (resp.code === 0) {
|
|
|
+ console.log('objectId -> ' + resp.data.objectId)
|
|
|
+ // this.signedUrl = resp.data
|
|
|
+ // console.log('signed url -> ' + this.signedUrl)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 上传失败回调
|
|
|
+ */
|
|
|
+ handleError(err) {
|
|
|
+ this.$message.error('文件上传失败,请重试: ' + err.message)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+/* 🎨 简单的视频美化样式 */
|
|
|
+.video-preview-wrapper {
|
|
|
+ margin-top: 20px;
|
|
|
+ padding: 15px;
|
|
|
+ background-color: #f8f9fa;
|
|
|
+ border-radius: 6px;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+ max-width: 600px; /* 限制最大宽度,防止大视频撑满全屏 */
|
|
|
+}
|
|
|
+
|
|
|
+.preview-title {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #606266;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.video-player {
|
|
|
+ border-radius: 4px;
|
|
|
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
|
+ outline: none;
|
|
|
+}
|
|
|
+</style>
|