add download for video

This commit is contained in:
RockYang 2024-08-26 07:24:04 +08:00
parent 4801832d9d
commit d601226187
8 changed files with 607 additions and 59 deletions

View File

@ -26,6 +26,7 @@ import (
"io"
"net/http"
"os"
"path/filepath"
"runtime/debug"
"strings"
"time"
@ -315,58 +316,65 @@ func staticResourceMiddleware() gin.HandlerFunc {
url := c.Request.URL.String()
// 拦截生成缩略图请求
if strings.HasPrefix(url, "/static/") && strings.Contains(url, "?imageView2") {
r := strings.SplitAfter(url, "imageView2")
size := strings.Split(r[1], "/")
if len(size) != 8 {
c.String(http.StatusNotFound, "invalid thumb args")
return
}
with := utils.IntValue(size[3], 0)
height := utils.IntValue(size[5], 0)
quality := utils.IntValue(size[7], 75)
if strings.HasPrefix(url, "/static/") {
if strings.Contains(url, "?imageView2") {
r := strings.SplitAfter(url, "imageView2")
size := strings.Split(r[1], "/")
if len(size) != 8 {
c.String(http.StatusNotFound, "invalid thumb args")
return
}
with := utils.IntValue(size[3], 0)
height := utils.IntValue(size[5], 0)
quality := utils.IntValue(size[7], 75)
// 打开图片文件
filePath := strings.TrimLeft(c.Request.URL.Path, "/")
file, err := os.Open(filePath)
if err != nil {
c.String(http.StatusNotFound, "Image not found")
return
}
defer file.Close()
// 打开图片文件
filePath := strings.TrimLeft(c.Request.URL.Path, "/")
file, err := os.Open(filePath)
if err != nil {
c.String(http.StatusNotFound, "Image not found")
return
}
defer file.Close()
// 解码图片
img, _, err := image.Decode(file)
// for .webp image
if err != nil {
img, err = webp.Decode(file)
}
if err != nil {
c.String(http.StatusInternalServerError, "Error decoding image")
return
// 解码图片
img, _, err := image.Decode(file)
// for .webp image
if err != nil {
img, err = webp.Decode(file)
}
if err != nil {
c.String(http.StatusInternalServerError, "Error decoding image")
return
}
var newImg image.Image
if height == 0 || with == 0 {
// 固定宽度,高度自适应
newImg = resize.Resize(uint(with), uint(height), img, resize.Lanczos3)
} else {
// 生成缩略图
newImg = resize.Thumbnail(uint(with), uint(height), img, resize.Lanczos3)
}
var buffer bytes.Buffer
err = jpeg.Encode(&buffer, newImg, &jpeg.Options{Quality: quality})
if err != nil {
logger.Error(err)
c.String(http.StatusInternalServerError, err.Error())
return
}
// 设置图片缓存有效期为一年 (365天)
c.Header("Cache-Control", "max-age=31536000, public")
// 直接输出图像数据流
c.Data(http.StatusOK, "image/jpeg", buffer.Bytes())
c.Abort() // 中断请求
} else if strings.Contains(url, "?download=true") {
filename := filepath.Base(url)
c.Header("Content-Disposition", "attachment; filename="+filename)
c.Header("Content-Type", "application/octet-stream")
}
var newImg image.Image
if height == 0 || with == 0 {
// 固定宽度,高度自适应
newImg = resize.Resize(uint(with), uint(height), img, resize.Lanczos3)
} else {
// 生成缩略图
newImg = resize.Thumbnail(uint(with), uint(height), img, resize.Lanczos3)
}
var buffer bytes.Buffer
err = jpeg.Encode(&buffer, newImg, &jpeg.Options{Quality: quality})
if err != nil {
logger.Error(err)
c.String(http.StatusInternalServerError, err.Error())
return
}
// 设置图片缓存有效期为一年 (365天)
c.Header("Cache-Control", "max-age=31536000, public")
// 直接输出图像数据流
c.Data(http.StatusOK, "image/jpeg", buffer.Bytes())
c.Abort() // 中断请求
}
c.Next()
}

154
web/src/assets/css/home.css Normal file
View File

@ -0,0 +1,154 @@
.home {
display: flex;
height: 100vh;
width: 100%;
flex-flow: column;
}
.home .header {
display: flex;
justify-content: space-between;
height: 50px;
line-height: 50px;
background-color: #1e1f22;
padding-right: 20px;
}
.home .header .banner {
display: flex;
}
.home .header .banner .logo {
display: flex;
padding: 5px;
cursor: pointer;
}
.home .header .banner .logo .el-image {
width: 48px;
height: 48px;
background-color: #fff;
border-radius: 50%;
}
.home .header .banner .title {
display: flex;
color: #fff;
font-size: 20px;
padding: 0 10px;
}
.home .header .navbar {
display: flex;
flex-flow: row;
}
.home .header .navbar .link-button {
margin-right: 15px;
color: #e1e1e1;
padding: 0 10px;
}
.home .header .navbar .link-button:hover {
background-color: #414141;
}
.home .header .navbar .link-button .iconfont {
font-size: 24px;
}
.home .header .navbar .user-info {
width: 100%;
padding: 5px 0;
}
.home .header .navbar .user-info .el-dropdown-link {
width: 100%;
cursor: pointer;
display: flex;
}
.home .header .navbar .user-info .el-dropdown-link .el-image {
width: 36px;
height: 36px;
border-radius: 50%;
}
.home .header .navbar .user-info .el-dropdown-link .el-icon {
color: #ccc;
line-height: 24px;
}
.home .main {
width: 100%;
display: flex;
flex-flow: row;
}
.home .main .navigator {
display: flex;
flex-flow: column;
width: 60px;
padding: 10px 1px;
border-right: 1px solid #3c3c3c;
background-color: #1e1f22;
}
.home .main .navigator .nav-items {
margin-top: 10px;
padding: 0 5px;
}
.home .main .navigator .nav-items li {
margin-bottom: 15px;
display: flex;
flex-flow: column;
}
.home .main .navigator .nav-items li a {
color: #dadbdc;
border-radius: 10px;
width: 48px;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
background-color: #414348;
}
.home .main .navigator .nav-items li a .el-image {
border-radius: 10px;
}
.home .main .navigator .nav-items li a .iconfont {
font-size: 20px;
}
.home .main .navigator .nav-items li a:hover,
.home .main .navigator .nav-items li a.active {
color: #47fff1;
background-color: #0f7a71;
}
.home .main .navigator .nav-items li .title {
font-size: 12px;
padding-top: 6px;
color: #e5e7eb;
text-align: center;
white-space: nowrap; /* 防止文本换行 */
overflow: hidden; /* 隐藏溢出内容 */
text-overflow: unset; /* 使用省略号表示溢出内容 */
}
.home .main .navigator .nav-items li .active {
color: #47fff1;
}
.home .main .content {
width: 100%;
overflow: auto;
box-sizing: border-box;
background-color: #282c34;
}
.el-popper .more-menus li {
padding: 10px 15px;
cursor: pointer;
border-radius: 5px;
margin: 5px 0;
}
.el-popper .more-menus li .el-image {
position: relative;
top: 5px;
right: 5px;
}
.el-popper .more-menus li:hover {
background-color: #f1f1f1;
}
.el-popper .more-menus li.active {
background-color: #f1f1f1;
}
.el-popper .user-info-menu li a {
width: 100%;
justify-content: left;
}
.el-popper .user-info-menu li a:hover {
text-decoration: none !important;
color: var(--el-primary-text-color);
}

View File

@ -0,0 +1,101 @@
.index-page {
margin: 0;
overflow: hidden;
color: #fff;
display: flex;
justify-content: center;
align-items: baseline;
padding-top: 150px;
}
.index-page .color-bg {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
}
.index-page .image-bg {
filter: blur(8px);
background-size: cover;
background-position: center;
}
.index-page .shadow {
box-shadow: rgba(0,0,0,0.3) 0px 0px 3px;
}
.index-page .shadow:hover {
box-shadow: rgba(0,0,0,0.3) 0px 0px 8px;
}
.index-page .menu-box {
position: absolute;
top: 0;
width: 100%;
display: flex;
}
.index-page .menu-box .el-menu {
padding: 0 30px;
width: 100%;
display: flex;
justify-content: space-between;
background: none;
border: none;
}
.index-page .menu-box .el-menu .menu-item {
display: flex;
padding: 20px 0;
color: #fff;
}
.index-page .menu-box .el-menu .menu-item .title {
font-size: 24px;
padding: 10px 10px 0 10px;
}
.index-page .menu-box .el-menu .menu-item .el-image {
height: 50px;
background-color: #fff;
}
.index-page .menu-box .el-menu .menu-item .el-button {
margin-left: 10px;
}
.index-page .menu-box .el-menu .menu-item .el-button span {
margin-left: 5px;
}
.index-page .content {
text-align: center;
position: relative;
display: flex;
flex-flow: column;
align-items: center;
}
.index-page .content h1 {
font-size: 5rem;
margin-bottom: 1rem;
}
.index-page .content p {
font-size: 1.5rem;
margin-bottom: 2rem;
}
.index-page .content .navs {
display: flex;
max-width: 900px;
padding: 20px;
}
.index-page .content .navs .el-space--horizontal {
justify-content: center;
}
.index-page .content .navs .nav-item {
width: 200px;
}
.index-page .content .navs .nav-item .el-button {
width: 100%;
padding: 25px 20px;
font-size: 1.3rem;
transition: all 0.3s ease;
}
.index-page .content .navs .nav-item .el-button .iconfont {
font-size: 24px;
margin-right: 10px;
position: relative;
top: -2px;
}
.index-page .footer .el-link__inner {
color: #fff;
}

View File

@ -0,0 +1,99 @@
.bg {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #313237;
background-image: url("~@/assets/img/login-bg.jpg");
background-size: cover;
background-position: center;
background-repeat: repeat-y;
}
.main .contain {
position: fixed;
left: 50%;
top: 40%;
width: 90%;
max-width: 400px;
transform: translate(-50%, -50%);
padding: 20px 10px;
color: #fff;
border-radius: 10px;
}
.main .contain .logo {
text-align: center;
}
.main .contain .logo .el-image {
width: 120px;
cursor: pointer;
background-color: #fff;
border-radius: 50%;
}
.main .contain .header {
width: 100%;
margin-bottom: 24px;
font-size: 24px;
color: $white_v1;
letter-space: 2px;
text-align: center;
padding-top: 10px;
}
.main .contain .content {
width: 100%;
height: auto;
border-radius: 3px;
}
.main .contain .content .block {
margin-bottom: 16px;
}
.main .contain .content .block .el-input__inner {
border: 1px solid $gray-v6 !important;
}
.main .contain .content .block .el-input__inner .el-icon-user,
.main .contain .content .block .el-input__inner .el-icon-lock {
font-size: 20px;
}
.main .contain .content .btn-row {
padding-top: 10px;
}
.main .contain .content .btn-row .login-btn {
width: 100%;
font-size: 16px;
letter-spacing: 2px;
}
.main .contain .content .text-line {
justify-content: center;
padding-top: 10px;
font-size: 14px;
}
.main .contain .content .opt {
padding: 15px;
}
.main .contain .content .opt .el-col {
text-align: center;
}
.main .contain .content .divider {
border-top: 2px solid #c1c1c1;
}
.main .contain .content .clogin {
padding: 15px;
display: flex;
justify-content: center;
}
.main .contain .content .clogin .iconfont {
font-size: 20px;
background: #e9f1f6;
padding: 8px;
border-radius: 50%;
cursor: pointer;
}
.main .contain .content .clogin .iconfont.icon-wechat {
color: #0bc15f;
}
.main .footer {
color: #fff;
}
.main .footer .container {
padding: 20px;
}

142
web/src/assets/css/luma.css Normal file
View File

@ -0,0 +1,142 @@
.page-luma {
display: flex;
height: 100%;
background-color: #0e0808;
overflow: auto;
flex-flow: column;
align-items: center;
background: linear-gradient(180deg, rgba(75,62,53,0.8), rgba(144,50,181,0.3));
}
.page-luma .prompt-box {
display: flex;
max-width: 56rem;
width: 100%;
padding: 20px;
flex-flow: column;
}
.page-luma .prompt-box .images {
display: flex;
flex-flow: row;
padding-bottom: 10px;
justify-content: center;
}
.page-luma .prompt-box .images .item {
position: relative;
}
.page-luma .prompt-box .images .item .el-image {
width: 100px;
height: 100px;
border-radius: 6px;
margin-right: 10px;
}
.page-luma .prompt-box .images .item .el-icon {
position: absolute;
cursor: pointer;
font-size: 20px;
color: #545454;
right: 10px;
top: 0;
}
.page-luma .prompt-box .images .item .el-icon:hover {
color: #888;
}
.page-luma .prompt-box .prompt-container {
width: 100%;
}
.page-luma .prompt-box .prompt-container .input-container {
background: linear-gradient(90deg, rgba(75,62,53,0.8), rgba(144,50,181,0.3));
border-radius: 28px;
padding: 10px 20px;
display: flex;
align-items: center;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
}
.page-luma .prompt-box .prompt-container .input-container .prompt-input {
background: transparent;
border: none;
outline: none;
color: #fff;
font-size: 14px;
width: 100%;
padding: 10px;
resize: none;
white-space: pre-wrap;
word-wrap: break-word;
line-height: 24px;
overflow-wrap: break-word;
scrollbar-width: none; /* 隐藏滚动条 */
}
.page-luma .prompt-box .prompt-container .input-container .prompt-input::placeholder {
color: rgba(255,255,255,0.6);
}
.page-luma .prompt-box .prompt-container .input-container .prompt-input::-webkit-scrollbar {
display: none;
}
.page-luma .prompt-box .prompt-container .input-container .upload-icon,
.page-luma .prompt-box .prompt-container .input-container .send-icon {
color: #e1e1e1;
}
.page-luma .prompt-box .prompt-container .input-container .upload-icon .iconfont,
.page-luma .prompt-box .prompt-container .input-container .send-icon .iconfont {
font-size: 20px;
cursor: pointer;
}
.page-luma .prompt-box .prompt-container .input-container .upload-icon {
position: relative;
}
.page-luma .video-container {
display: flex;
flex-flow: column;
width: 100%;
padding: 0 40px;
}
.page-luma .video-container .h-title {
color: #fff;
width: 100%;
font-size: 36px;
text-align: left;
}
.page-luma .video-container .videos .item {
margin-bottom: 20px;
}
.page-luma .video-container .videos .item .video-box {
width: 100%;
border-radius: 10px;
}
.page-luma .video-container .videos .item .video-box video,
.page-luma .video-container .videos .item .video-box img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 10px;
cursor: pointer;
}
.page-luma .video-container .videos .item .video-name {
color: #e1e1e1;
font-size: 16px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding: 6px 0;
text-align: center;
}
.page-luma .video-container .videos .item .opts {
display: flex;
justify-content: center;
}
.page-luma .video-container .videos .item .opts .btn {
margin-right: 10px;
background-color: rgba(255,255,255,0.15);
border: none;
border-radius: 20px;
padding: 3px 15px;
cursor: pointer;
color: #fff;
font-size: 14px;
}
.page-luma .video-container .videos .item .opts .btn .iconfont {
font-size: 12px;
}
.page-luma .video-container .videos .item .opts .btn:hover {
background-color: rgba(255,255,255,0.2);
}

View File

@ -110,10 +110,8 @@
}
.videos {
display flex
flex-flow row
.item {
margin-bottom 20px
.video-box {
width 100%
@ -127,6 +125,7 @@
}
}
.video-name {
color #e1e1e1
font-size 16px
@ -136,6 +135,29 @@
padding 6px 0
text-align center
}
.opts {
display flex
justify-content center
.btn {
margin-right 10px
background-color hsla(0,0%,100%,.15)
border none
border-radius 20px
padding 3px 15px
cursor pointer
color #ffffff
font-size 14px
.iconfont {
font-size 12px
}
&:hover {
background-color hsla(0,0%,100%,.2)
}
}
}
}
}
}

View File

@ -20,7 +20,7 @@
:auto-upload="true"
:show-file-list="false"
:http-request="afterRead"
accept=".doc,.docx,.jpg,.png,.jpeg,.xls,.xlsx,.ppt,.pptx,.pdf"
accept=".doc,.docx,.jpg,.png,.jpeg,.xls,.xlsx,.ppt,.pptx,.pdf,.mp4,.mp3"
>
<el-icon class="avatar-uploader-icon">
<Plus/>

View File

@ -33,12 +33,20 @@
<el-row :gutter="20" class="videos">
<el-col :span="8" class="item" :key="item.id" v-for="item in videos">
<div class="video-box" @mouseover="item.playing = true" @mouseout="item.playing = false">
<img :src="item.cover" :alt="item.name" v-if="!item.playing"/>
<video :src="item.url" preload="auto" :autoplay="true" loop="loop" muted="muted" v-else>
<img :src="item.cover" :alt="item.name" v-show="!item.playing"/>
<video :src="item.url" preload="auto" :autoplay="true" loop="loop" muted="muted" v-show="item.playing">
您的浏览器不支持视频播放
</video>
</div>
<div class="video-name">{{item.name}}</div>
<div class="opts">
<a :href="item.url+'?download=true'" download="video.mp4">
<button class="btn">
<i class="iconfont icon-download"></i>
<span>下载</span>
</button>
</a>
</div>
</el-col>
</el-row>
</el-container>
@ -60,24 +68,38 @@ const videos = ref([
{
id: 1,
name: 'a dancing girl',
url: 'http://localhost/download/xmind.mp4',
url: 'http://localhost:5678/static/upload/2024/8/1724574661747320.mp4',
cover: 'https://storage.cdn-luma.com/dream_machine/d133794f-3124-4059-a9f2-e5fed79f0d5b/video_0_thumb.jpg',
playing: false
},
{
id: 1,
name: 'a dancing girl',
url: 'http://localhost/download/dancing.mp4',
name: 'a dancing girl a dancing girl a dancing girl a dancing girl a dancing girl',
url: 'https://storage.cdn-luma.com/dream_machine/92efa55a-f381-4161-a999-54f8fe460fca/watermarked_video0e5aad607a0644c66850d1d77022db847.mp4',
cover: 'https://storage.cdn-luma.com/dream_machine/92efa55a-f381-4161-a999-54f8fe460fca/video_1_thumb.jpg',
playing: false
},
{
id: 1,
name: 'a dancing girl',
url: 'http://localhost/download/xmind.mp4',
url: 'https://storage.cdn-luma.com/dream_machine/d133794f-3124-4059-a9f2-e5fed79f0d5b/watermarked_video01944f69966f14d33b6c4486a8cfb8dde.mp4',
cover: 'https://storage.cdn-luma.com/dream_machine/d133794f-3124-4059-a9f2-e5fed79f0d5b/video_0_thumb.jpg',
playing: false
}
},
{
id: 1,
name: 'a dancing girl',
url: 'https://storage.cdn-luma.com/dream_machine/92efa55a-f381-4161-a999-54f8fe460fca/watermarked_video0e5aad607a0644c66850d1d77022db847.mp4',
cover: 'https://storage.cdn-luma.com/dream_machine/92efa55a-f381-4161-a999-54f8fe460fca/video_1_thumb.jpg',
playing: false
},
{
id: 1,
name: 'a dancing girl',
url: 'https://storage.cdn-luma.com/dream_machine/d133794f-3124-4059-a9f2-e5fed79f0d5b/watermarked_video01944f69966f14d33b6c4486a8cfb8dde.mp4',
cover: 'https://storage.cdn-luma.com/dream_machine/d133794f-3124-4059-a9f2-e5fed79f0d5b/video_0_thumb.jpg',
playing: false
},
])