SpringBoot3 + Element-Plus + Vue3 + TS 实现上传、拖拽上传到阿里云OSS视频、图片、文件以及对图片、视频的预览和删除OSS端图片(最新)
element
A Vue.js 2.0 UI Toolkit for Web
项目地址:https://gitcode.com/gh_mirrors/eleme/element
免费下载资源
·
SpringBoot3 + Element-Plus + Vue3 + TS 实现上传、拖拽上传到阿里云OSS视频、图片、文件以及对图片、视频的预览和删除OSS端图片(最新)
1、效果展示
1.1 界面展示
1.2 操作展示
1.3 上传展示
1.4 视频预览
1.5 图片预览
2、Vue3 实现代码
2.1 UploadFiles.vue
<!--
* @Date: 2024-04-18 11:23:45
* @LastEditors: zhong
* @LastEditTime: 2024-04-20 15:13:44
* @FilePath: \app-admin\src\components\UploadFile\UploadFiles.vue
-->
<template>
<div class="file">
<el-upload class="upload-demo" drag action="http://127.0.0.1:9999/api/file/upload"
accept=".jpg,.jpeg,.png,.gif,.mp4,.avi" :on-success="fileSuccess" multiple :on-preview="handlePictureCardPreview"
:on-remove="handleRemove" v-model:file-list="filePathList" :before-upload="beforeUpload"
:on-progress="handleProgress" list-type="picture-card">
<template #file="{ file }">
<template v-if="(/\.(mp4|avi|mov)$/i).test(file.url)" class="video-container">
<video ref="currentVideo" class="preview-video video-player" controls>
<source :src="file.url" type="video/mp4" />
</video>
<label class="el-upload-list__item-status-label"><i
class="el-icon el-icon--upload-success el-icon--check"><svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 1024">
<path fill="currentColor"
d="M406.656 706.944 195.84 496.256a32 32 0 1 0-45.248 45.248l256 256 512-512a32 32 0 0 0-45.248-45.248L406.592 706.944z">
</path>
</svg></i></label>
<i class="el-icon el-icon--close"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<path fill="currentColor"
d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z">
</path>
</svg></i>
<i class="el-icon--close-tip">按 delete 键可删除</i>
<span class="el-upload-list__item-actions">
<span class="el-upload-list__item-preview" @click="playVideo(file.url)"><i class="el-icon el-icon--zoom-in">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<path fill="currentColor"
d="m795.904 750.72 124.992 124.928a32 32 0 0 1-45.248 45.248L750.656 795.904a416 416 0 1 1 45.248-45.248zM480 832a352 352 0 1 0 0-704 352 352 0 0 0 0 704m-32-384v-96a32 32 0 0 1 64 0v96h96a32 32 0 0 1 0 64h-96v96a32 32 0 0 1-64 0v-96h-96a32 32 0 0 1 0-64z">
</path>
</svg></i>
</span>
<span class="el-upload-list__item-delete" @click="deleteMove(file)"><i class="el-icon el-icon--delete">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<path fill="currentColor"
d="M160 256H96a32 32 0 0 1 0-64h256V95.936a32 32 0 0 1 32-32h256a32 32 0 0 1 32 32V192h256a32 32 0 1 1 0 64h-64v672a32 32 0 0 1-32 32H192a32 32 0 0 1-32-32zm448-64v-64H416v64zM224 896h576V256H224zm192-128a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32m192 0a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32">
</path>
</svg></i>
</span>
</span>
</template>
</template>
<el-icon class="el-icon--upload cloud"><upload-filled /></el-icon>
<div class="el-upload__text" style="line-height: 22px;">
Drop file here or <em>click to upload</em>
</div>
<!-- <template #tip>
<div class="el-upload__tip">
files with a size less than 100MB
</div>
</template> -->
</el-upload>
</div>
<el-dialog v-model="dialogVisibleVideo" style="text-align: center;" title="视频预览">
<video class="preview-video video-player" width="400px" controls>
<source :src="dialogVideoUrl" type="video/mp4" />
</video>
</el-dialog>
<el-dialog v-model="dialogVisibleImage" style="text-align: center;" title="图片预览">
<img w-full :src="dialogImageUrl" alt="Preview Image" width="400px" height="auto">
</el-dialog>
</template>
<script setup lang="ts">
import { UploadFilled } from '@element-plus/icons-vue'
import { ref, Ref } from 'vue';
import { ElMessage, type UploadProps } from 'element-plus'
import { deleteFileApi } from '@/api/upLoadFile/upLoadFile';
interface Video {
name: string;
url: string;
}
const filePathList: Ref<Video[]> = ref([
{
name: "a",
url: "https://img1.baidu.com/it/u=836505309,2852067569&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=499"
},
{
name: "b",
url: "https://b0.bdstatic.com/1ea54e9fbe51ef1ef7a7f7a9efc97335.jpg@h_1280"
}
])
// 判断上传文件大小
const beforeUpload = (file: File) => {
const isLt10M = file.size / 1024 / 1024 < 100;
if (!isLt10M) {
ElMessage.error('上传文件大小不能超过 100MB!');
}
return isLt10M;
};
// 上传进度
const handleProgress = (event: ProgressEvent, file: File) => {
const percentage = ((event.loaded / event.total) * 100).toFixed(2);
ElMessage.success(`文件 ${file.name} 上传中:${percentage}%`);
};
// 上传文件成功写入List
const fileSuccess = (res: any) => {
filePathList.value[filePathList.value.length - 1].url = res.data;
}
// 查看视频
const dialogVideoUrl = ref('')
const dialogVisibleVideo = ref(false)
const playVideo = (url: string) => {
// 更新当前视频 URL
dialogVideoUrl.value = url;
dialogVisibleVideo.value = true;
}
// 删除视频
const deleteMove = async (file: any) => {
filePathList.value = filePathList.value.filter(item => item.url !== file.url);
// 实际删除oss函数
let url = file.url;
const lastSlashIndex = url.lastIndexOf('/');
let name = url.substring(url.lastIndexOf('/') + 1);
let date = url.substring(url.lastIndexOf('/', lastSlashIndex - 1), lastSlashIndex)
let res = await deleteFileApi(date + "," + name);
if (res && res.code == 200) {
// 信息提示
ElMessage.success(res.msg);
} else {
ElMessage.error("删除失败,请重试!");
}
}
const dialogImageUrl = ref('')
const dialogVisibleImage = ref(false)
// 删除照片
const handleRemove: UploadProps['onRemove'] = async (uploadFile) => {
// 获取 url参数
let url: string = <string>uploadFile.url;
// 实际删除oss函数
// 找到日期部分的起始位置(倒数第二个斜杠的下一个位置)
const lastSlashIndex = url.lastIndexOf('/');
let name = url.substring(url.lastIndexOf('/') + 1);
let date = url.substring(url.lastIndexOf('/', lastSlashIndex - 1), lastSlashIndex)
let res = await deleteFileApi(date + "," + name);
if (res && res.code == 200) {
// 信息提示
ElMessage.success(res.msg);
} else {
ElMessage.error("删除失败,请重试!");
}
}
// 查看照片
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
dialogImageUrl.value = uploadFile.url!
dialogVisibleImage.value = true;
}
</script>
<style lang="scss" scoped>
::v-deep(.el-upload-dragger) {
height: 148px !important;
padding-top: 10px;
padding-bottom: 0px;
margin-bottom: 0px;
}
::v-deep(.cloud) {
margin-bottom: 0px;
}
.file {
display: flex;
flex-wrap: wrap;
}
.upload-demo {
width: 100%;
.el-upload__tip {
text-align: center;
}
}
.video-player {
width: 100%;
/* 视频宽度占满容器 */
height: 100%;
/* 视频高度占满容器 */
object-fit: cover;
/* 使用 cover 保持视频比例,可能会裁剪视频 */
}
</style>
2.2 UseUpLoadFile.vue
<template>
<UploadFiles></UploadFiles>
</template>
<script setup lang="ts">
import UploadFiles from '@/components/UploadFile/UploadFiles.vue';
</script>
2.3 upLoadFile.ts
/*
* @Date: 2024-04-18 12:35:49
* @LastEditors: zhong
* @LastEditTime: 2024-04-20 14:27:08
* @FilePath: \app-admin\src\api\uploadFile\upLoadFile.ts
*/
import http from "@/http";
// 新增
export const ossUploadFileApi = () => {
return http.put(`/api/file/upload/`);
}
//删除图片
export const deleteFileApi = (fileName: string) => {
console.log(fileName);
return http.put(`/api/file/upload/${fileName}`);
}
2.4 http.ts
/*
* @Date: 2024-03-30 12:37:05
* @LastEditors: zhong
* @LastEditTime: 2024-04-16 20:27:33
* @FilePath: \app-admin\src\http\index.ts
*/
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from "axios";
import { ElMessage } from "element-plus";
// axios 请求配置
const config = {
// baseURL:'http://localhost:8080',
baseURL: '/api',
timeout: 1000
}
// 定义返回值类型
export interface Result<T = any> {
code: number;
msg: string;
data: T;
}
class Http {
// axios 实例
private instance: AxiosInstance;
// 构造函数初始化
constructor(config: AxiosRequestConfig) {
this.instance = axios.create(config);
//定义拦截器
this.interceptors();
}
private interceptors() {
// axios 发送请求之前的处理
this.instance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
// 在请求头部携带token
// let token = sessionStorage.getItem('token');
let token = '';
if (token) {
config.headers!['token'] = token;
// 把 token 放到 headers 里面
// (config.headers as AxiosRequestHeaders).token = token;
}
// console.log(config);
return config;
}, (error: any) => {
error.data = {};
error.data.msg = '服务器异常,请联系管理员!'
return error;
})
// axios 请求返回之后的处理
// 请求返回处理
this.instance.interceptors.response.use((res: AxiosResponse) => {
// console.log(res.data);
if (res.data.code != 200) {
ElMessage.error(res.data.msg || '服务器出错啦');
return Promise.reject(res.data.msg || '服务器出错啦');
} else {
return res.data;
}
}, (error) => {
console.log('进入错误!');
error.data = {};
if (error && error.response) {
switch (error.response.status) {
case 400:
error.data.msg = "错误请求";
ElMessage.error(error.data.msg);
break;
case 401:
error.data.msg = "未授权,请登录";
ElMessage.error(error.data.msg);
break;
case 403:
error.data.msg = "拒绝访问";
ElMessage.error(error.data.msg);
break;
case 404:
error.data.msg = "请求错误,未找到该资源";
ElMessage.error(error.data.msg);
break;
case 405:
error.data.msg = "请求方法未允许";
ElMessage.error(error.data.msg);
break;
case 408:
error.data.msg = "请求超时";
ElMessage.error(error.data.msg);
break;
case 500:
error.data.msg = "服务器端出错";
ElMessage.error(error.data.msg);
break;
case 501:
error.data.msg = "网络未实现";
ElMessage.error(error.data.msg);
break;
case 502:
error.data.msg = "网络错误";
ElMessage.error(error.data.msg);
break;
case 503:
error.data.msg = "服务不可用";
ElMessage.error(error.data.msg);
break;
case 504:
error.data.msg = "网络超时";
ElMessage.error(error.data.msg);
break;
case 505:
error.data.msg = "http版本不支持该请求";
ElMessage.error(error.data.msg);
break;
default:
error.data.msg = `连接错误${error.response.status}`;
ElMessage.error(error.data.msg);
}
} else {
error.data.msg = "连接到服务器失败";
ElMessage.error(error.data.msg)
}
return Promise.reject(error);
})
}
// GET方法
get<T = Result>(url: string, params?: object): Promise<T> {
return this.instance.get(url, { params });
}
// POST方法
post<T = Result>(url: string, data?: object): Promise<T> {
return this.instance.post(url, data);
}
// PUT方法
put<T = Result>(url: string, data?: object): Promise<T> {
return this.instance.put(url, data );
}
// DELETE方法
delete<T = Result>(url: string): Promise<T> {
return this.instance.delete(url);
}
}
export default new Http(config);
3、SpringBoot 实现代码
3.1 FileUploadController.java
package com.zhx.app.controller;
import com.zhx.app.utils.AliOssUtil;
import com.zhx.app.utils.ResultUtils;
import com.zhx.app.utils.ResultVo;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.UUID;
/**
* @ClassName : FileUploadController
* @Description : 文件上传相关操作
* @Author : zhx
* @Date: 2024-03-01 19:45
*/
@RestController
@RequestMapping("/api/file")
public class FileUploadController {
@PostMapping("/upload")
public ResultVo upLoadFile(MultipartFile file) throws Exception {
// 获取文件原名
String originalFilename = file.getOriginalFilename();
// 防止重复上传文件名重复
String fileName = null;
if (originalFilename != null) {
fileName = UUID.randomUUID() + originalFilename.substring(originalFilename.indexOf("."));
}
// 把文件储存到本地磁盘
// file.transferTo(new File("E:\\SpringBootBase\\ProjectOne\\big-event\\src\\main\\resources\\flies\\" + fileName));
String url = AliOssUtil.uploadFile(fileName, file.getInputStream());
return ResultUtils.success("上传成功!", url);
}
@PutMapping("/upload/{fileName}")
public ResultVo deleteFile(@PathVariable("fileName") String fileName) {
System.out.println(fileName);
if (fileName != null) {
return AliOssUtil.deleteFile(fileName);
}
return ResultUtils.success("上传失败!");
}
}
3.2 AliOssUtil.java
package com.zhx.app.utils;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @ClassName : AliOssUtil
* @Description : 阿里云上传服务
* @Author : zhx
* @Date: 2024-03-1 20:29
*/
@Component
public class AliOssUtil {
private static String ENDPOINT;
@Value("${alioss.endpoint}")
public void setENDPOINT(String endpoint) {
ENDPOINT = endpoint;
}
private static String ACCESS_KEY;
@Value("${alioss.access_key}")
public void setAccessKey(String accessKey) {
ACCESS_KEY = accessKey;
}
private static String ACCESS_KEY_SECRET;
@Value("${alioss.access_key_secret}")
public void setAccessKeySecret(String accessKeySecret) {
ACCESS_KEY_SECRET = accessKeySecret;
}
private static String BUCKETNAME;
@Value("${alioss.bucketName}")
public void setBUCKETNAME(String bucketName) {
BUCKETNAME = bucketName;
}
public static String uploadFile(String objectName, InputStream inputStream) {
String url = "";
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY, ACCESS_KEY_SECRET);
try {
// 创建PutObjectRequest对象。
// 生成日期文件夹路径,例如:2022/04/18
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd");
String dateStr = dateFormat.format(new Date());
PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKETNAME, dateStr + "/" + objectName, inputStream);
// 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
// ObjectMetadata metadata = new ObjectMetadata();
// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
// metadata.setObjectAcl(CannedAccessControlList.Private);
// putObjectRequest.setMetadata(metadata);
// 上传文件。
PutObjectResult result = ossClient.putObject(putObjectRequest);
url = "https://" + BUCKETNAME + "." + ENDPOINT.substring(ENDPOINT.lastIndexOf("/") + 1) + "/" + dateStr + "/" + objectName;
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
return url;
}
public static ResultVo deleteFile(String objectName) {
System.out.println(objectName);
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY, ACCESS_KEY_SECRET);
try {
// 删除文件。
System.out.println(objectName);
System.out.println(objectName.replace(",", "/"));
ossClient.deleteObject(BUCKETNAME, objectName.replace(",", "/"));
return ResultUtils.success("删除成功!");
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
return ResultUtils.error("上传失败!");
}
}
3.3 yml
spring:
# 设置文件上传大小
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
# 阿里云配置
alioss:
endpoint: "https://oss-cn-beijing.aliyuncs.com" # Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
bucketName: "**********" # 填写Bucket名称,例如examplebucket。
access_key: "********************" # 点击头像->Accesskey管理查看 秘钥
access_key_secret: "********************" # 密码
GitHub 加速计划 / eleme / element
54.06 K
14.63 K
下载
A Vue.js 2.0 UI Toolkit for Web
最近提交(Master分支:3 个月前 )
c345bb45
7 个月前
a07f3a59
* Update transition.md
* Update table.md
* Update transition.md
* Update table.md
* Update transition.md
* Update table.md
* Update table.md
* Update transition.md
* Update popover.md 7 个月前
更多推荐
已为社区贡献4条内容
所有评论(0)