当前位置: 首页 > news >正文

视频格式网络地址转换视频到本地,获取封面、时长,其他格式转换成mp4

使用ffmpeg软件转换网络视频,先从官网下载对应操作系统环境的包

注意:网络地址需要是视频格式结尾,例如.mp4,.flv 等

官网地址:Download FFmpeg     

window包:

415b5111089d42a4a02116b3fb877065.png

linux包:

39729fa04ffc4bf895ce22971e583292.png

如果下载缓慢,下载迅雷安装使用下载。

解压缩后对应截图:

window:

4cc5ed2dd51f4e6f9c96cb846d54e438.png

linux:

16eb35142ad44511b8c0089bd9437a56.png

在maven项目的pom.xml引入依赖包:

 <dependency><groupId>net.bramp.ffmpeg</groupId><artifactId>ffmpeg</artifactId><version>0.7.0</version></dependency><dependency><groupId>org.bytedeco</groupId><artifactId>javacpp</artifactId><version>1.4.1</version></dependency><dependency><groupId>org.bytedeco</groupId><artifactId>javacv</artifactId><version>1.4.1</version></dependency><dependency><groupId>org.bytedeco.javacpp-presets</groupId><artifactId>ffmpeg-platform</artifactId><version>3.4.2-1.4.1</version></dependency>

引入类:

import cn.hutool.core.date.DateUtil;
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 org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

网络地址转换成本地视频方法:

 /*** 视频链接转换成本地视频* @param videoUrl* @param downloadPath* @return*/public static boolean downVideo(String videoUrl,String downloadPath){HttpURLConnection connection = null;InputStream inputStream = null;RandomAccessFile randomAccessFile = null;boolean re;try{URL url = new URL(videoUrl);connection = (HttpURLConnection) url.openConnection();connection.setRequestProperty("Range","bytes=0-");connection.connect();if (connection.getResponseCode() / 100 != 2){System.out.println("链接失败");return  false;}inputStream = connection.getInputStream();int downloaded = 0;int fileSize = connection.getContentLength();randomAccessFile = new RandomAccessFile(downloadPath,"rw");while (downloaded < fileSize){byte[] buffer = null;if (fileSize - downloaded >= 1000000){buffer = new byte[1000000];}else{buffer = new byte[fileSize - downloaded];}int read = -1;int currentDownload = 0;while (currentDownload < buffer.length){read = inputStream.read();buffer[currentDownload++] = (byte) read;}randomAccessFile.write(buffer);downloaded += currentDownload;}re = true;return re;} catch (Exception e) {e.printStackTrace();re = false;return re;}finally {try{connection.disconnect();inputStream.close();randomAccessFile.close();}catch (Exception e){e.printStackTrace();}}}

网站地址转换成本地视频后,再转换成mp4视频方法:

/*** 其他视频格式地址转换成mp4* @param orginalVideoPath 原视频地址* @param newMp4FilePath 新mp4地址* @return*/public static boolean otherVideoToMp4(String orginalVideoPath,String newMp4FilePath)  {try{String ffmpegPath = "";String ffprobePath = "";if (SystemUtils.isWindows()){//目录里放的文件没有提交保存,在本地测试的时候自行添加ffmpegPath = VideoCovertUtil.class.getResource("/ffmpegdir/win/bin/ffmpeg.exe").getPath();ffprobePath = VideoCovertUtil.class.getResource("/ffmpegdir/win/bin/ffprobe.exe").getPath();}else if (SystemUtils.isLinux()){/*ffmpegPath = VideoCovertUtil.class.getResource("/ffmpegdir/linux/ffmpeg").getPath();ffprobePath = VideoCovertUtil.class.getResource("/ffmpegdir/linux/ffprobe").getPath();*///在linux安装ffmpeg后配置路径//安装步骤:https://blog.csdn.net/ysushiwei/article/details/130162831ffmpegPath = "/usr/local/bin/ffmpeg";ffprobePath = "/usr/local/bin/ffprobe";}log.info("ffmpegPath:"+ffmpegPath);log.info("ffmpegPath:"+ffprobePath);FFmpeg fFmpeg = new FFmpeg(ffmpegPath);FFprobe fFprobe = new FFprobe(ffprobePath);FFmpegBuilder builder = new FFmpegBuilder().setInput(orginalVideoPath).addOutput(newMp4FilePath).done();FFmpegExecutor executor = new FFmpegExecutor(fFmpeg,fFprobe);executor.createJob(builder).run();log.info("执行完毕");return  true;}catch (IOException e){e.printStackTrace();return false;}}

window可以直接放在项目中,但是linux还需要配置。步骤如下。

1、将上方的linux包上传到服务器,解压缩:

tar -xvf ffmpeg-release-amd64-static.tar.xz

2、解压缩后进入根目录分别复制根目录下的ffmpeg和ffprobe到 /usr/local/bin/目录下:

sudo cp 解压缩目录/ffmpeg /usr/local/bin/
sudo cp 解压缩目录/ffprobe /usr/local/bin/

3.还要给文件设置权限,否则运行代码的时候报没有权限:

sudo chmod +x /usr/local/bin/ffmpeg

sudo chmod +x /usr/local/bin/ffprobe

4、最后检查是否配置成功,如果有内容输出来则成功:

ffmpeg -version

ffprobe -version

linux环境配置好后,即可正常解析.

从视频中提取封面和获取时长:

 /*** 获取视频的第一帧封面* @param filePath 视频地址* @param targetPath 视频封面地址*/public static void getCover(String filePath,String targetPath){try{// 视频地址FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(new File(filePath));grabber.start();Java2DFrameConverter converter = new Java2DFrameConverter();BufferedImage image = converter.convert(grabber.grabImage());// 本地图片保存地址ImageIO.write(image, "png", new File(targetPath));grabber.stop();image.flush();}catch (Exception e){e.printStackTrace();}}/*** 使用FFmpeg获取视频时长** @param path 视频文件地址* @return 时长,单位为秒* @throws IOException*/public static String getDuration(String path) {// 读取视频文件FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(path);try {grabber.start();} catch (FrameGrabber.Exception e) {e.printStackTrace();}// 获取视频长度(单位:秒)int duration = grabber.getLengthInFrames() / (int) grabber.getFrameRate();try {grabber.stop();} catch (FrameGrabber.Exception e) {e.printStackTrace();}return DateUtil.secondToTime(duration);}

由于视频转换下载等速度比较慢,推荐使用异步执行。我用的是若依的框架,代码如下。如用其他框架,可自行参考写异步操作

//异步执行方法。不会等待执行完才执行下一位
AsyncManager.me().execute(AsyncFactory.convertVideoNetUrl(video.getVideoPath(),video.getId(),Constants.CONVERT_VIDEO_NET_VIDEO_URL));#在AsyncManager类里自定义一个异步方法如下/*** * @param videNetUrl 视频网络地址* @param id 类id* @param entityClazz 类 0:视频 1:文章* @return 任务task*/public static TimerTask convertVideoNetUrl(final String videNetUrl,Long id,Integer entityClazz){return new TimerTask(){@Overridepublic void run(){if (entityClazz == null || id == null || StrUtil.isBlank(videNetUrl)){return;}if (entityClazz == 0){IVideoService videoService =  SpringUtils.getBean(IVideoService.class);Video video = videoService.selectVideoById(id);if (video == null){return;}//现在是上传视频地址//先转换视频地址到服务器//后缀String ext = video.getVideoPath().substring(video.getVideoPath().lastIndexOf("."));String videosubpath = StringUtils.format("{}/{}_{}{}", DateUtils.datePath(),IdUtils.fastSimpleUUID(), Seq.getId(Seq.uploadSeqType), ext);String downloadPath = null;try {downloadPath = FileUploadUtils.getAbsoluteFile(HqaConfig.getUploadPath() + "/", videosubpath).getAbsolutePath();}catch (Exception e){e.printStackTrace();}boolean downVideo = VideoCovertUtil.downVideo(video.getVideoPath(),downloadPath);if (downVideo && StrUtil.isNotBlank(downloadPath) && downloadPath != null){if (!ext.contains("mp4")){//下载成功后如果不是mp4格式,转换成mp4格式String newVideosubpath = StringUtils.format("{}/{}_{}{}", DateUtils.datePath(),IdUtils.fastSimpleUUID(), Seq.getId(Seq.uploadSeqType), ".mp4");String newMp4FilePath = null;try {newMp4FilePath = FileUploadUtils.getAbsoluteFile(HqaConfig.getUploadPath() + "/", newVideosubpath).getAbsolutePath();}catch (Exception e){e.printStackTrace();}boolean toMp4 = VideoCovertUtil.otherVideoToMp4(downloadPath,newMp4FilePath);if (toMp4 && StrUtil.isNotBlank(newMp4FilePath) && newMp4FilePath != null){//转换成功后删除之前下载过的视频地址,并且保存新的mp4地址if (new File(downloadPath).exists()){FileUtils.deleteFile(downloadPath);}if (newMp4FilePath.contains("\\")){newMp4FilePath = newMp4FilePath.replace("\\","/");}String newPath = newMp4FilePath.replace(HqaConfig.getProfile(),"/profile");video.setVideoPath(newPath);}}else{if (downloadPath.contains("\\")){downloadPath = downloadPath.replace("\\","/");}//保存地址String newPath = downloadPath.replace(HqaConfig.getProfile(),"/profile");video.setVideoPath(newPath);}//视频截图和时长//获取视频第一帧封面String parentPath = HqaConfig.getUploadPath()+"/"+ DateUtils.datePath();String fileName = IdUtils.fastSimpleUUID()+".png";String targetPath = parentPath+"/"+ fileName;try {FileUploadUtils.getAbsoluteFile(parentPath,fileName);} catch (IOException e) {e.printStackTrace();}String filePath = video.getVideoPath().replace("/profile","");filePath=HqaConfig.getProfile()+filePath;VideoCovertUtil.getCover(filePath,targetPath);video.setCover(targetPath.replace(HqaConfig.getProfile(),"/profile"));String  duration = VideoCovertUtil.getDuration(filePath);video.setDuration(duration);videoService.updateVideo(video);}}else if (entityClazz == 1){IArticleService articleService = SpringUtils.getBean(IArticleService.class);Article article = articleService.selectArticleById(id);if (article == null){return;}//现在是上传视频地址//先转换视频地址到服务器//后缀String ext = article.getVideoPath().substring(article.getVideoPath().lastIndexOf("."));String videosubpath = StringUtils.format("{}/{}_{}{}", DateUtils.datePath(),IdUtils.fastSimpleUUID(), Seq.getId(Seq.uploadSeqType), ext);String downloadPath = null;try {downloadPath = FileUploadUtils.getAbsoluteFile(HqaConfig.getUploadPath() + "/", videosubpath).getAbsolutePath();}catch (Exception e){e.printStackTrace();}boolean downVideo = VideoCovertUtil.downVideo(article.getVideoPath(),downloadPath);if (downVideo && StrUtil.isNotBlank(downloadPath) && downloadPath != null){if (!ext.contains("mp4")){//下载成功后如果不是mp4格式,转换成mp4格式String newVideosubpath = StringUtils.format("{}/{}_{}{}", DateUtils.datePath(),IdUtils.fastSimpleUUID(), Seq.getId(Seq.uploadSeqType), ".mp4");String newMp4FilePath = null;try {newMp4FilePath = FileUploadUtils.getAbsoluteFile(HqaConfig.getUploadPath() + "/", newVideosubpath).getAbsolutePath();}catch (Exception e){e.printStackTrace();}boolean toMp4 = VideoCovertUtil.otherVideoToMp4(downloadPath,newMp4FilePath);if (toMp4 && StrUtil.isNotBlank(newMp4FilePath) && newMp4FilePath != null){//转换成功后删除之前下载过的视频地址,并且保存新的mp4地址if (new File(downloadPath).exists()){FileUtils.deleteFile(downloadPath);}if (newMp4FilePath.contains("\\")){newMp4FilePath = newMp4FilePath.replace("\\","/");}String newPath = newMp4FilePath.replace(HqaConfig.getProfile(),"/profile");article.setVideoPath(newPath);}}else{if (downloadPath.contains("\\")){downloadPath = downloadPath.replace("\\","/");}//保存地址String newPath = downloadPath.replace(HqaConfig.getProfile(),"/profile");article.setVideoPath(newPath);}articleService.updateArticle(article);}}}};}

相关文章:

视频格式网络地址转换视频到本地,获取封面、时长,其他格式转换成mp4

使用ffmpeg软件转换网络视频&#xff0c;先从官网下载对应操作系统环境的包 注意:网络地址需要是视频格式结尾&#xff0c;例如.mp4,.flv 等 官网地址&#xff1a;Download FFmpeg window包&#xff1a; linux包&#xff1a; 如果下载缓慢&#xff0c;下载迅雷安装使用…...

企业私有云容器化架构运维实战

什么是虚拟化: 虚拟化&#xff08;Virtualization&#xff09;技术最早出现在 20 世纪 60 年代的 IBM 大型机系统&#xff0c;在70年代的 System 370 系列中逐渐流行起来&#xff0c;这些机器通过一种叫虚拟机监控器&#xff08;Virtual Machine Monitor&#xff0c;VMM&#x…...

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用UserSet功能保存和载入相机的各类参数(C++)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用UserSet功能保存和载入相机的各类参数&#xff08;C&#xff09; Baumer工业相机Baumer工业相机NEOAPISDK中UserSet的技术背景代码案例分享第一步&#xff1a;保存相机当前参数设置UserSet_Save第二步&#xff1a;载入已经保存…...

STM32的以太网外设+PHY(LAN8720)使用详解(3):PHY寄存器详解

0 工具准备 1.野火 stm32f407霸天虎开发板 2.LAN8720数据手册 3.STM32F4xx中文参考手册1 PHY寄存器 前面介绍到&#xff0c;站管理接口&#xff08;SMI&#xff09;允许应用程序通过2线时钟和数据线访问任意PHY寄存器&#xff0c;同时该接口支持访问最多32个PHY&#xff0c;也…...

缓存和缓冲的区别

近期被这两个词汇困扰了&#xff0c;感觉有本质的区别&#xff0c;搜了一些资料&#xff0c;整理如下 计算机内部的几个部分图如下 缓存&#xff08;cache&#xff09; https://baike.baidu.com/item/%E7%BC%93%E5%AD%98 提到缓存&#xff08;cache&#xff09;&#xff0c;就…...

C++高级-STL库概述

目录 一、概念 二、STL的历史背景 三、STL的版本 四、STL的主要优势...

uniapp 统一获取授权提示和48小时间隔授权

应用商店审核要求 获取权限前需要给提示&#xff0c;拒绝之后48小时不能给弹窗授权 项目用的是uniapp getImagePermission(v?: string, tag?: any, source?: any, proj?: any) {// proj proj || vueSelf.$proj(tag, source);let data {state: false,//是否原生授权denied…...

Halcon点云重建

dev_close_window () *点云文件数据的读取 read_object_model_3d (‘E:/1.om3’, ‘mm’, [], [], ObjectModel3D, Status) *获得点云的数据&#xff0c;例如高度 get_object_model_3d_params (ObjectModel3D, ‘point_coord_z’, GenParamValue) dev_open_window (0, 0, 512, …...

docker学习(二十一、network使用示例container、自定义)

文章目录 一、container应用示例1.需要共用同一个端口的服务&#xff0c;不适用container方式2.可用示例3.停掉共享源的容器&#xff0c;其他容器只有本地回环lo地址 总结 二、自定义网络应用示例默认bridge&#xff0c;容器间ip通信默认bridge&#xff0c;容器间服务名不通 自…...

【Python机器学习系列】一文带你了解机器学习中的Pipeline管道机制(理论+源码)

这是Python机器学习原创文章&#xff0c;我的第183篇原创文章。 一、引言 对于表格数据&#xff0c;一套完整的机器学习建模流程如下&#xff1a; 背景知识1&#xff1a;机器学习中的学习器 【Python机器学习系列】一文搞懂机器学习中的转换器和估计器&#xff08;附案例&…...

算法基础之整数划分

整数划分 核心思想&#xff1a; 计数类dp 背包做法 f[i][j] 表示 取 1 – i 的物品 总容量为j的选法数量 f[i][j] f[i-1][j] f[i-1][j-v[i]] f[i-1][j-2v[i]] f[i-1][j-3v[i]] ……f[i-1][j-kv[i]] f[i][j-v[i]] f[i-1][j-v[i]] f[i-1][j-2v[i]] f[i-1][j-3v[i]] ……f[i…...

关于“Python”的核心知识点整理大全47

目录 16.1.10 错误检查 highs_lows.py highs_lows.py 16.2 制作世界人口地图&#xff1a;JSON 格式 16.2.1 下载世界人口数据 16.2.2 提取相关的数据 population_data.json world_population.py 16.2.3 将字符串转换为数字值 world_population.py 2world_population…...

Android 8.1 设置USB传输文件模式(MTP)

项目需求&#xff0c;需要在电脑端adb发送通知手机端接收指令&#xff0c;将USB的仅充电模式更改成传输文件&#xff08;MTP&#xff09;模式&#xff0c;便捷用户在我的电脑里操作内存文件&#xff0c;下面是我们的常见的修改方式 1、android12以下、android21以上是这种方式…...

模型量化 | Pytorch的模型量化基础

官方网站&#xff1a;Quantization — PyTorch 2.1 documentation Practical Quantization in PyTorch | PyTorch 量化简介 量化是指执行计算和存储的技术 位宽低于浮点精度的张量。量化模型 在张量上执行部分或全部操作&#xff0c;精度降低&#xff0c;而不是 全精度&#xf…...

adb和logcat常用命令

adb的作用 adb构成 client端&#xff0c;在电脑上&#xff0c;负责发送adb命令daemon守护进程adbd&#xff0c;在手机上&#xff0c;负责接收和执行adb命令server端&#xff0c;在电脑上&#xff0c;负责管理client和daemon之间的通信 adb工作原理 client端将命令发送给ser…...

千巡翼X4轻型无人机 赋能智慧矿山

千巡翼X4轻型无人机 赋能智慧矿山 传统的矿山测绘需要大量测绘员通过采用手持RTK、全站仪对被测区域进行外业工作&#xff0c;再通过方格网法、三角网法、断面法等进行计算&#xff0c;需要耗费大量人力和时间。随着无人机航测技术的不断发展&#xff0c;利用无人机作业可以大…...

【Android 13】使用Android Studio调试系统应用之Settings移植(一):编译服务器的配置、AOSP源码的下载、编译、运行

文章目录 1. 篇头语2. 系列文章3. ubuntu 最佳版本3.1 下载并安装3.2 配置AOSP工具链3.3 配置Python多版本支持4. AOSP源码下载4.1 配置repo工具4.2 源码下载5. AOSP编译5.1 添加emulator模拟器配置5.1.1 哪些是支持模拟器的Products?5.1.2 添加方法5.2 编译...

【1】Docker详解与部署微服务实战

Docker 详解 Docker 简介 Docker 是一个开源的容器化平台&#xff0c;可以帮助开发者将应用程序和其依赖的环境打包成一个可移植、可部署的容器。Docker 的主要目标是通过容器化技术实现应用程序的快速部署、可移植性和可扩展性&#xff0c;从而简化应用程序的开发、测试和部…...

C# JsonString转Object以及Object转JsonString

主要讲述了两种方法的转换&#xff0c;最后提供了格式化输出JsonString字符串。 需要引用程序集 System.Web.Extensions.dll、Newtonsoft.Json.dll System.Web.Extensions.dll可直接在程序集中引用&#xff0c;Newtonsoft.Json.dll需要在NuGet中下载引用。 详细代码&#xf…...

华为OD机试真题-中文分词模拟器-2023年OD统一考试(C卷)

题目描述: 给定一个连续不包含空格字符串,该字符串仅包含英文小写字母及英文文标点符号(逗号、分号、句号),同时给定词库,对该字符串进行精确分词。 说明: 1.精确分词: 字符串分词后,不会出现重叠。即“ilovechina” ,不同词库可分割为 “i,love,china” “ilove,c…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...