Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
basic-api-boot
概览
概览
详情
活动
周期分析
版本库
存储库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
问题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
Basic
basic-api-boot
Commits
47dbe6cd
提交
47dbe6cd
authored
2月 14, 2023
作者:
方治民
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: 采用本地环境 ffmpeg + wrap 方式处理音视频,减少依赖包大小
上级
075e438f
隐藏空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
132 行增加
和
118 行删除
+132
-118
build.gradle
app/build.gradle
+0
-4
UploadProcessServiceImpl.java
...m/yiring/app/service/upload/UploadProcessServiceImpl.java
+108
-106
Minio.java
...mon/minio/src/main/java/com/yiring/common/core/Minio.java
+4
-1
UploadProcessService.java
.../java/com/yiring/common/service/UploadProcessService.java
+3
-3
FileManageServiceImpl.java
...com/yiring/common/service/impl/FileManageServiceImpl.java
+17
-4
没有找到文件。
app/build.gradle
浏览文件 @
47dbe6cd
...
@@ -54,10 +54,6 @@ dependencies {
...
@@ -54,10 +54,6 @@ dependencies {
implementation
"io.minio:minio:${minioVersion}"
implementation
"io.minio:minio:${minioVersion}"
implementation
"com.squareup.okhttp3:okhttp:${okhttpVersion}"
implementation
"com.squareup.okhttp3:okhttp:${okhttpVersion}"
// Optional: 扩展实现在文件上传时对文件进行预处理,依赖 Minio 模块
// Optional: 扩展实现在文件上传时对文件进行预处理,依赖 Minio 模块
// https://mvnrepository.com/artifact/org.bytedeco/javacv
implementation
'org.bytedeco:javacv:1.5.7'
// https://mvnrepository.com/artifact/org.bytedeco/ffmpeg-platform
implementation
'org.bytedeco:ffmpeg-platform:5.0-1.5.7'
// https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox
// https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox
implementation
"org.apache.pdfbox:pdfbox:${pdfboxVersion}"
implementation
"org.apache.pdfbox:pdfbox:${pdfboxVersion}"
// https://mvnrepository.com/artifact/net.bramp.ffmpeg/ffmpeg
// https://mvnrepository.com/artifact/net.bramp.ffmpeg/ffmpeg
...
...
app/src/main/java/com/yiring/app/service/upload/UploadProcessServiceImpl.java
浏览文件 @
47dbe6cd
...
@@ -2,17 +2,17 @@
...
@@ -2,17 +2,17 @@
package
com
.
yiring
.
app
.
service
.
upload
;
package
com
.
yiring
.
app
.
service
.
upload
;
import
cn.hutool.core.io.FileUtil
;
import
cn.hutool.core.io.FileUtil
;
import
com.yiring.common.config.EnvConfig
;
import
com.yiring.common.core.Minio
;
import
com.yiring.common.core.Minio
;
import
com.yiring.common.service.UploadProcessService
;
import
com.yiring.common.service.UploadProcessService
;
import
com.yiring.common.util.Commons
;
import
io.minio.ObjectWriteResponse
;
import
io.minio.ObjectWriteResponse
;
import
java.awt.image.BufferedImage
;
import
java.awt.image.BufferedImage
;
import
java.awt.image.RenderedImage
;
import
java.io.ByteArrayInputStream
;
import
java.io.ByteArrayInputStream
;
import
java.io.ByteArrayOutputStream
;
import
java.io.ByteArrayOutputStream
;
import
java.io.File
;
import
java.io.File
InputStream
;
import
java.io.InputStream
;
import
java.io.InputStream
;
import
java.nio.charset.StandardCharsets
;
import
java.nio.charset.StandardCharsets
;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
import
java.nio.file.Path
;
import
java.nio.file.Paths
;
import
java.nio.file.Paths
;
import
java.util.List
;
import
java.util.List
;
...
@@ -22,9 +22,14 @@ import lombok.Cleanup;
...
@@ -22,9 +22,14 @@ import lombok.Cleanup;
import
lombok.RequiredArgsConstructor
;
import
lombok.RequiredArgsConstructor
;
import
lombok.SneakyThrows
;
import
lombok.SneakyThrows
;
import
lombok.extern.slf4j.Slf4j
;
import
lombok.extern.slf4j.Slf4j
;
import
net.bramp.ffmpeg.FFmpeg
;
import
net.bramp.ffmpeg.FFmpegExecutor
;
import
net.bramp.ffmpeg.FFprobe
;
import
net.bramp.ffmpeg.builder.FFmpegBuilder
;
import
net.bramp.ffmpeg.probe.FFmpegFormat
;
import
net.bramp.ffmpeg.probe.FFmpegProbeResult
;
import
org.apache.pdfbox.pdmodel.PDDocument
;
import
org.apache.pdfbox.pdmodel.PDDocument
;
import
org.apache.pdfbox.rendering.PDFRenderer
;
import
org.apache.pdfbox.rendering.PDFRenderer
;
import
org.bytedeco.javacv.*
;
import
org.springframework.context.annotation.Primary
;
import
org.springframework.context.annotation.Primary
;
import
org.springframework.http.MediaType
;
import
org.springframework.http.MediaType
;
import
org.springframework.stereotype.Component
;
import
org.springframework.stereotype.Component
;
...
@@ -42,11 +47,15 @@ import org.springframework.stereotype.Component;
...
@@ -42,11 +47,15 @@ import org.springframework.stereotype.Component;
public
class
UploadProcessServiceImpl
implements
UploadProcessService
{
public
class
UploadProcessServiceImpl
implements
UploadProcessService
{
final
Minio
minio
;
final
Minio
minio
;
final
EnvConfig
env
;
Pattern
pattern
=
Pattern
.
compile
(
"^.*\\.ts$"
);
Pattern
pattern
=
Pattern
.
compile
(
"^.*\\.ts$"
);
@SneakyThrows
@Override
@Override
public
String
handle
(
String
object
,
InputStream
is
)
{
public
String
handle
(
String
object
,
Path
path
)
{
@Cleanup
FileInputStream
is
=
new
FileInputStream
(
path
.
toFile
());
String
suffix
=
FileUtil
.
getSuffix
(
object
);
String
suffix
=
FileUtil
.
getSuffix
(
object
);
// Image: 在文件名上追加图片物理像素
// Image: 在文件名上追加图片物理像素
...
@@ -61,7 +70,7 @@ public class UploadProcessServiceImpl implements UploadProcessService {
...
@@ -61,7 +70,7 @@ public class UploadProcessServiceImpl implements UploadProcessService {
// Video/Audio: 在文件名上追加时长,视频生成封面图
// Video/Audio: 在文件名上追加时长,视频生成封面图
if
(
isSupportiveMedia
(
suffix
))
{
if
(
isSupportiveMedia
(
suffix
))
{
object
=
handleMedia
(
object
,
suffix
,
is
);
object
=
handleMedia
(
object
,
suffix
,
path
);
}
}
return
object
;
return
object
;
...
@@ -98,123 +107,111 @@ public class UploadProcessServiceImpl implements UploadProcessService {
...
@@ -98,123 +107,111 @@ public class UploadProcessServiceImpl implements UploadProcessService {
}
}
@SneakyThrows
@SneakyThrows
public
String
handleMedia
(
String
object
,
String
suffix
,
InputStream
is
)
{
public
String
handleMedia
(
String
object
,
String
suffix
,
Path
path
)
{
// 判断是否为视频
// 判断是否配置 ffmpeg 环境
@Cleanup
FFmpeg
ffmpeg
=
new
FFmpeg
();
FFmpegFrameGrabber
ff
=
new
FFmpegFrameGrabber
(
is
);
FFprobe
ffprobe
=
new
FFprobe
();
ff
.
start
();
if
(!
ffmpeg
.
isFFmpeg
()
||
!
ffprobe
.
isFFprobe
())
{
return
object
;
}
// 解析媒体文件
FFmpegProbeResult
probeResult
=
ffprobe
.
probe
(
path
.
toString
());
FFmpegFormat
format
=
probeResult
.
getFormat
();
// 构建具有时长(秒)标记的存储地址
// 构建具有时长(秒)标记的存储地址
String
filepath
=
fillSuffix
(
object
,
"T"
+
(
ff
.
getLengthInTime
()
/
(
1000
*
1000
)));
long
sec
=
(
long
)
format
.
duration
;
String
filepath
=
fillSuffix
(
object
,
"T"
+
sec
);
// 视频截取首帧可见画面作为封面
// 视频截取首帧可见画面作为封面
if
(
isSupportiveVideo
(
suffix
))
{
if
(
isSupportiveVideo
(
suffix
))
{
// 获取视频可见画面帧
// 使用 ffmpeg 截取视频首帧图片
Frame
frame
=
getPictureFrame
(
ff
);
Path
tempFile
=
Paths
.
get
(
path
.
getParent
().
toString
(),
FileUtil
.
getName
(
filepath
)
+
".jpg"
);
FFmpegBuilder
builder
=
new
FFmpegBuilder
()
// 将截取的封面并存储
.
setInput
(
path
.
toString
())
String
format
=
"jpg"
;
.
overrideOutputFiles
(
true
)
@Cleanup
.
addOutput
(
tempFile
.
toString
())
ByteArrayOutputStream
os
=
new
ByteArrayOutputStream
();
.
setFrames
(
1
)
ImageIO
.
write
(
frameToBufferedImage
(
frame
),
format
,
os
);
.
done
();
@Cleanup
FFmpegExecutor
executor
=
new
FFmpegExecutor
(
ffmpeg
,
ffprobe
);
InputStream
io
=
new
ByteArrayInputStream
(
os
.
toByteArray
());
executor
.
createJob
(
builder
).
run
();
int
size
=
io
.
available
();
minio
.
putObject
(
io
,
MediaType
.
IMAGE_JPEG_VALUE
,
filepath
+
"."
+
format
);
// 判断封面图是否生成
if
(
Files
.
exists
(
tempFile
))
{
// 上传封面图
minio
.
putObject
(
tempFile
.
toFile
(),
getObjectFolder
(
filepath
));
}
// 大视频文件切片上传(>
10M
)
// 大视频文件切片上传(>
5s
)
if
(
s
ize
>
(
10
*
10
*
1024
)
)
{
if
(
s
ec
>
5
)
{
filepath
=
fillSuffix
(
handleToM3u8
(
object
,
suffix
,
ff
),
"T"
+
(
ff
.
getLengthInTime
()
/
(
1000
*
1000
))
);
filepath
=
handleToHls
(
filepath
,
path
,
ffmpeg
,
ffprobe
);
}
}
}
}
ff
.
stop
();
return
filepath
;
return
filepath
;
}
}
@SneakyThrows
@SneakyThrows
public
String
handleToM3u8
(
String
object
,
String
suffix
,
FFmpegFrameGrabber
ff
)
{
public
String
handleToHls
(
String
object
,
Path
path
,
FFmpeg
ffmpeg
,
FFprobe
ffprobe
)
{
File
tmpDir
=
FileUtil
.
getTmpDir
();
String
originName
=
FileUtil
.
getName
(
object
);
String
sourceName
=
FileUtil
.
getName
(
object
);
String
objectFolder
=
object
.
replace
(
"/"
+
originName
,
""
);
String
objectFolder
=
object
.
replace
(
"/"
+
sourceName
,
""
);
String
targetName
=
originName
.
replaceAll
(
"^(.*)\\."
+
FileUtil
.
getSuffix
(
originName
)
+
"$"
,
"$1.m3u8"
);
Path
m3u8
=
Paths
.
get
(
tmpDir
.
getPath
(),
// 使用 ffmpeg 将视频文件转换成 hls 切片文件 m3u8+ts
Commons
.
uuid
(),
// "-vsync", "2", "-c:v", "copy", "-c:a", "copy", "-tune", "fastdecode", "-hls_wrap", "0", "-hls_time", "10", "-hls_list_size", "0", "-threads", "12"
sourceName
.
replaceAll
(
"^(.*)\\."
+
suffix
+
"$"
,
"$1.m3u8"
)
final
FFmpegProbeResult
probe
=
ffprobe
.
probe
(
path
.
toString
());
);
Path
tempFile
=
Paths
.
get
(
path
.
getParent
().
toString
(),
targetName
);
FileUtil
.
mkParentDirs
(
m3u8
);
FFmpegBuilder
builder
=
new
FFmpegBuilder
()
String
out
=
m3u8
.
toFile
().
getPath
();
.
setInput
(
path
.
toString
())
.
overrideOutputFiles
(
true
)
long
start
=
System
.
currentTimeMillis
();
.
addOutput
(
tempFile
.
toString
())
@Cleanup
.
setFormat
(
probe
.
getFormat
().
format_name
)
FFmpegFrameRecorder
recorder
=
new
FFmpegFrameRecorder
(
.
setStrict
(
FFmpegBuilder
.
Strict
.
STRICT
)
out
,
.
setFormat
(
"hls"
)
ff
.
getImageWidth
(),
.
setPreset
(
"ultrafast"
)
ff
.
getImageHeight
(),
.
addExtraArgs
(
ff
.
getAudioChannels
()
"-vsync"
,
);
"2"
,
recorder
.
setFormat
(
"hls"
);
"-c:v"
,
recorder
.
setOption
(
"hls_wrap"
,
"0"
);
"copy"
,
recorder
.
setOption
(
"hls_time"
,
"5"
);
"-c:a"
,
recorder
.
setOption
(
"hls_list_size"
,
"0"
);
"copy"
,
recorder
.
setOption
(
"hls_flags"
,
"delete_segments"
);
"-tune"
,
recorder
.
setOption
(
"hls_segment_type"
,
"mpegts"
);
"fastdecode"
,
recorder
.
setOption
(
"hls_segment_filename"
,
out
.
replace
(
".m3u8"
,
"-%d.ts"
));
"-hls_wrap"
,
recorder
.
setOption
(
"hls_delete_threshold"
,
"1"
);
"0"
,
recorder
.
setOption
(
"vsync"
,
"2"
);
"-hls_time"
,
recorder
.
setOption
(
"c:v"
,
"copy"
);
"5"
,
recorder
.
setOption
(
"c:a"
,
"copy"
);
"-hls_list_size"
,
recorder
.
setOption
(
"tune"
,
"fastdecode"
);
"0"
,
recorder
.
setOption
(
"threads"
,
"8"
);
"-hls_segment_filename"
,
recorder
.
start
();
tempFile
.
toString
().
replaceAll
(
"^(.*)\\.m3u8$"
,
"$1-%d.ts"
),
Frame
frame
;
"-threads"
,
while
((
frame
=
ff
.
grabImage
())
!=
null
)
{
"8"
try
{
)
recorder
.
record
(
frame
);
.
done
();
}
catch
(
FrameRecorder
.
Exception
e
)
{
FFmpegExecutor
executor
=
new
FFmpegExecutor
(
ffmpeg
,
ffprobe
);
log
.
error
(
e
.
getMessage
(),
e
);
executor
.
createJob
(
builder
).
run
();
if
(
Files
.
exists
(
tempFile
))
{
// 解析 m3u8 文件
List
<
String
>
lines
=
FileUtil
.
readLines
(
tempFile
.
toString
(),
StandardCharsets
.
UTF_8
);
// 获取 ts 切片文件
List
<
String
>
tss
=
lines
.
stream
().
filter
(
line
->
pattern
.
matcher
(
line
).
matches
()).
toList
();
// 上传 ts 切片文件
for
(
String
ts
:
tss
)
{
Path
temp
=
Paths
.
get
(
tempFile
.
getParent
().
toString
(),
ts
);
minio
.
putObject
(
temp
.
toFile
(),
objectFolder
);
}
}
}
// 上传 m3u8 索引文件
recorder
.
setTimestamp
(
ff
.
getTimestamp
());
ObjectWriteResponse
objectWriteResponse
=
minio
.
putObject
(
tempFile
.
toFile
(),
objectFolder
);
recorder
.
flush
();
object
=
objectWriteResponse
.
object
();
long
end
=
System
.
currentTimeMillis
();
long
times
=
end
-
start
;
log
.
info
(
"[Times] {}: {} ms"
,
"video convert to m3u8"
,
times
);
// 解析 m3u8 文件
List
<
String
>
lines
=
FileUtil
.
readLines
(
m3u8
.
toString
(),
StandardCharsets
.
UTF_8
);
// 获取 ts 切片文件
List
<
String
>
tss
=
lines
.
stream
().
filter
(
line
->
pattern
.
matcher
(
line
).
matches
()).
toList
();
// 上传 ts 切片文件
for
(
String
ts
:
tss
)
{
Path
path
=
Paths
.
get
(
m3u8
.
getParent
().
toString
(),
ts
);
minio
.
putObject
(
path
.
toFile
(),
objectFolder
);
}
// 上传 m3u8 索引文件
ObjectWriteResponse
objectWriteResponse
=
minio
.
putObject
(
m3u8
.
toFile
(),
objectFolder
);
return
objectWriteResponse
.
object
();
}
public
static
Frame
getPictureFrame
(
FFmpegFrameGrabber
grabber
)
throws
FFmpegFrameGrabber
.
Exception
{
// 手动 GC 一次
int
ftp
=
grabber
.
getLengthInFrames
();
System
.
gc
();
int
flag
=
0
;
Frame
frame
=
null
;
while
(
flag
<=
ftp
)
{
// 获取帧
frame
=
grabber
.
grabImage
();
// 过滤前3帧,避免出现全黑图片
if
((
flag
>
3
)
&&
(
frame
!=
null
))
{
break
;
}
flag
++;
}
}
return
frame
;
}
public
static
RenderedImage
frameToBufferedImage
(
Frame
frame
)
{
return
object
;
@Cleanup
Java2DFrameConverter
converter
=
new
Java2DFrameConverter
();
return
converter
.
getBufferedImage
(
frame
);
}
}
public
static
boolean
isSupportiveMedia
(
String
suffix
)
{
public
static
boolean
isSupportiveMedia
(
String
suffix
)
{
...
@@ -242,4 +239,9 @@ public class UploadProcessServiceImpl implements UploadProcessService {
...
@@ -242,4 +239,9 @@ public class UploadProcessServiceImpl implements UploadProcessService {
String
regex
=
"^(.*)\\."
+
suffix
+
"$"
;
String
regex
=
"^(.*)\\."
+
suffix
+
"$"
;
return
object
.
replaceAll
(
regex
,
"$1."
+
fill
+
"."
+
suffix
);
return
object
.
replaceAll
(
regex
,
"$1."
+
fill
+
"."
+
suffix
);
}
}
public
static
String
getObjectFolder
(
String
object
)
{
String
sourceName
=
FileUtil
.
getName
(
object
);
return
object
.
replace
(
"/"
+
sourceName
,
""
);
}
}
}
basic-common/minio/src/main/java/com/yiring/common/core/Minio.java
浏览文件 @
47dbe6cd
...
@@ -12,6 +12,7 @@ import java.nio.file.Path;
...
@@ -12,6 +12,7 @@ import java.nio.file.Path;
import
java.time.LocalDateTime
;
import
java.time.LocalDateTime
;
import
java.time.format.DateTimeFormatter
;
import
java.time.format.DateTimeFormatter
;
import
java.util.Objects
;
import
java.util.Objects
;
import
lombok.Cleanup
;
import
lombok.NonNull
;
import
lombok.NonNull
;
import
org.springframework.stereotype.Component
;
import
org.springframework.stereotype.Component
;
import
org.springframework.web.multipart.MultipartFile
;
import
org.springframework.web.multipart.MultipartFile
;
...
@@ -106,7 +107,9 @@ public record Minio(MinioConfig config, MinioClient client) {
...
@@ -106,7 +107,9 @@ public record Minio(MinioConfig config, MinioClient client) {
}
}
Path
path
=
file
.
toPath
();
Path
path
=
file
.
toPath
();
return
putObject
(
Files
.
newInputStream
(
path
),
Files
.
probeContentType
(
path
),
object
);
@Cleanup
InputStream
inputStream
=
Files
.
newInputStream
(
path
);
return
putObject
(
inputStream
,
Files
.
probeContentType
(
path
),
object
);
}
}
/**
/**
...
...
basic-common/minio/src/main/java/com/yiring/common/service/UploadProcessService.java
浏览文件 @
47dbe6cd
/* (C) 2022 YiRing, Inc. */
/* (C) 2022 YiRing, Inc. */
package
com
.
yiring
.
common
.
service
;
package
com
.
yiring
.
common
.
service
;
import
java.
io.InputStream
;
import
java.
nio.file.Path
;
/**
/**
* 文件上传媒体文件预处理服务
* 文件上传媒体文件预处理服务
...
@@ -19,10 +19,10 @@ public interface UploadProcessService {
...
@@ -19,10 +19,10 @@ public interface UploadProcessService {
* 视频封面:haha.mp4 -> haha.mp4.jpg 截取视频封面
* 视频封面:haha.mp4 -> haha.mp4.jpg 截取视频封面
*
*
* @param object 上传文件存储地址
* @param object 上传文件存储地址
* @param
is 文件流
* @param
path 文件地址
* @return 预处理后的文件地址(可能对文件名追加了时长、页数、分辨率等标识)
* @return 预处理后的文件地址(可能对文件名追加了时长、页数、分辨率等标识)
*/
*/
default
String
handle
(
String
object
,
InputStream
is
)
{
default
String
handle
(
String
object
,
Path
path
)
{
return
object
;
return
object
;
}
}
}
}
basic-common/minio/src/main/java/com/yiring/common/service/impl/FileManageServiceImpl.java
浏览文件 @
47dbe6cd
/* (C) 2023 YiRing, Inc. */
/* (C) 2023 YiRing, Inc. */
package
com
.
yiring
.
common
.
service
.
impl
;
package
com
.
yiring
.
common
.
service
.
impl
;
import
cn.hutool.core.io.FileUtil
;
import
cn.hutool.core.util.IdUtil
;
import
cn.hutool.core.util.IdUtil
;
import
cn.hutool.core.util.StrUtil
;
import
cn.hutool.core.util.StrUtil
;
import
com.yiring.common.core.Minio
;
import
com.yiring.common.core.Minio
;
import
com.yiring.common.service.FileManageService
;
import
com.yiring.common.service.FileManageService
;
import
com.yiring.common.service.UploadProcessService
;
import
com.yiring.common.service.UploadProcessService
;
import
com.yiring.common.util.Commons
;
import
java.nio.file.Path
;
import
java.nio.file.Paths
;
import
lombok.RequiredArgsConstructor
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.stereotype.Service
;
import
org.springframework.stereotype.Service
;
...
@@ -39,11 +43,20 @@ public class FileManageServiceImpl implements FileManageService {
...
@@ -39,11 +43,20 @@ public class FileManageServiceImpl implements FileManageService {
String
uuid
=
IdUtil
.
fastSimpleUUID
();
String
uuid
=
IdUtil
.
fastSimpleUUID
();
String
object
=
minio
.
buildUploadPath
(
filename
,
""
,
uuid
);
String
object
=
minio
.
buildUploadPath
(
filename
,
""
,
uuid
);
// 将上传的文件转存一份到本地临时目录
Path
tempFile
=
Paths
.
get
(
FileUtil
.
getTmpDirPath
(),
Commons
.
uuid
(),
filename
);
FileUtil
.
mkParentDirs
(
tempFile
);
file
.
transferTo
(
tempFile
);
// 预处理(默认不做任何处理,具体逻辑需自行在外部实现)
// 预处理(默认不做任何处理,具体逻辑需自行在外部实现)
object
=
service
.
handle
(
object
,
file
.
getInputStream
());
object
=
service
.
handle
(
object
,
tempFile
);
// 删除为本次上传进行本地处理所创建的整个临时文件夹目录
// 上传原文件
FileUtil
.
del
(
tempFile
.
getParent
());
minio
.
putObject
(
file
.
getInputStream
(),
file
.
getContentType
(),
object
);
// 上传原文件(如果是转换成了 m3u8 hls 文件则不保存原文件)
String
suffix
=
".m3u8"
;
if
(
filename
.
endsWith
(
suffix
)
||
!
object
.
endsWith
(
suffix
))
{
minio
.
putObject
(
file
.
getInputStream
(),
file
.
getContentType
(),
object
);
}
return
minio
.
getDefaultURI
(
object
);
return
minio
.
getDefaultURI
(
object
);
}
}
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论