Java使用FFmpeg对视频文件打标记
免安装 FFmpeg
<dependency><groupId>ws.schild</groupId><artifactId>jave-all-deps</artifactId><version>3.0.1</version><exclusions><!-- 排除windows 32位系统 --><exclusion><groupId>ws.schild</groupId><artifactId>jave-nativebin-win32</artifactId></exclusion><!-- 排除linux 32位系统 --><exclusion><groupId>ws.schild</groupId><artifactId>jave-nativebin-linux32</artifactId></exclusion><!-- 排除Mac系统--><exclusion><groupId>ws.schild</groupId><artifactId>jave-nativebin-osx64</artifactId></exclusion></exclusions>
</dependency>
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ws.schild.jave.Encoder;
import ws.schild.jave.EncoderException;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.encode.AudioAttributes;
import ws.schild.jave.encode.EncodingAttributes;
import ws.schild.jave.encode.VideoAttributes;
import ws.schild.jave.info.MultimediaInfo;
import ws.schild.jave.process.ProcessWrapper;
import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.util.List;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;/*** @author Mr.superbeyone* @project * @className FfmpegUtil* @description* @date 2023-10-19 13:37**/
public class FfmpegUtil {private static Logger logger = LoggerFactory.getLogger(FfmpegUtil.class);/*** 通过本地路径获取多媒体文件信息(宽,高,时长,编码等)** @param localPath 本地路径* @return MultimediaInfo 对象,包含 (宽,高,时长,编码等)* @throws EncoderException*/public static MultimediaInfo getMultimediaInfo(String localPath) {MultimediaInfo multimediaInfo = null;try {multimediaInfo = new MultimediaObject(new File(localPath)).getInfo();} catch (EncoderException e) {System.out.println("获取多媒体文件信息异常");e.printStackTrace();}return multimediaInfo;}/*** 通过URL获取多媒体文件信息** @param url 网络url* @return MultimediaInfo 对象,包含 (宽,高,时长,编码等)* @throws EncoderException*/public static MultimediaInfo getMultimediaInfoFromUrl(String url) {MultimediaInfo multimediaInfo = null;try {multimediaInfo = new MultimediaObject(new URL(url)).getInfo();} catch (Exception e) {System.out.println("获取多媒体文件信息异常");e.printStackTrace();}return multimediaInfo;}private static final int SAMPLING_RATE = 16000;private static final int SINGLE_CHANNEL = 1;/*** 音频格式化为wav,并设置单声道和采样率** @param url 需要转格式的音频* @param targetPath 格式化后要保存的目标路径*/public static boolean formatAudio(String url, String targetPath) {File target = new File(targetPath);MultimediaObject multimediaObject;try {// 若是本地文件: multimediaObject = new MultimediaObject(new File("你的本地路径"));multimediaObject = new MultimediaObject(new URL(url));// 音频参数// 此处按需自定义音频参数AudioAttributes audio = new AudioAttributes();// 采样率audio.setSamplingRate(SAMPLING_RATE);// 单声道audio.setChannels(SINGLE_CHANNEL);Encoder encoder = new Encoder();EncodingAttributes attrs = new EncodingAttributes();// 输出格式attrs.setOutputFormat("wav");attrs.setAudioAttributes(audio);encoder.encode(multimediaObject, target, attrs);return true;} catch (Exception e) {System.out.println("格式化音频异常");return false;}}/*** 视频格式化为mp4** @param url* @param targetPath* @return*/public static boolean formatToMp4(String url, String targetPath) {File target = new File(targetPath);MultimediaObject multimediaObject;try {// 若是本地文件: multimediaObject = new MultimediaObject(new File("你的本地路径"));multimediaObject = new MultimediaObject(new URL(url));EncodingAttributes attributes = new EncodingAttributes();// 设置视频的音频参数AudioAttributes audioAttributes = new AudioAttributes();attributes.setAudioAttributes(audioAttributes);// 设置视频的视频参数VideoAttributes videoAttributes = new VideoAttributes();// 设置帧率videoAttributes.setFrameRate(25);attributes.setVideoAttributes(videoAttributes);// 设置输出格式attributes.setOutputFormat("mp4");Encoder encoder = new Encoder();encoder.encode(multimediaObject, target, attributes);return true;} catch (Exception e) {System.out.println("格式化视频异常");e.printStackTrace();return false;}}/*** 获取视频缩略图 获取视频第0秒的第一帧图片** <p>执行的ffmpeg 命令为: ffmpeg -i 你的视频文件路径 -ss 指定的秒数 生成文件的全路径地址** @param localPath 本地路径* @param targetPath 存放的目标路径* @return*/public static boolean getTargetThumbnail(String localPath, String targetPath) {// 该方法基本可作为执行ffmpeg命令的模板方法,之后的几个方法与此类似try {ProcessWrapper ffmpeg = new DefaultFFMPEGLocator().createExecutor();ffmpeg.addArgument("-i");ffmpeg.addArgument(localPath);ffmpeg.addArgument("-ss");// 此处可自定义视频的秒数ffmpeg.addArgument("10");ffmpeg.addArgument(targetPath);ffmpeg.execute();try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {blockFfmpeg(br);}} catch (IOException e) {System.out.println("获取视频缩略图失败");e.printStackTrace();return false;}return true;}/*** 等待命令执行成功,退出** @param br* @throws IOException*/private static void blockFfmpeg(BufferedReader br) throws IOException {String line;// 该方法阻塞线程,直至合成成功while ((line = br.readLine()) != null) {doNothing(line);}}/*** 打印日志** @param line*/private static void doNothing(String line) {// 正式使用时注释掉此行,仅用于观察日志
// System.out.println(line);}/*** 视频增加字幕** @param originVideoPath 原视频地址* @param targetVideoPath 目标视频地址* @param srtPath 固定格式的srt文件地址或存储位置,字母文件名: xxx.srt,样例看博客* @return* @throws Exception*/public static boolean addSubtitle(String originVideoPath, String srtPath, String targetVideoPath) {try {ProcessWrapper ffmpeg = new DefaultFFMPEGLocator().createExecutor();ffmpeg.addArgument("-i");ffmpeg.addArgument(originVideoPath);ffmpeg.addArgument("-i");ffmpeg.addArgument(srtPath);ffmpeg.addArgument("-c");ffmpeg.addArgument("copy");ffmpeg.addArgument(targetVideoPath);ffmpeg.execute();try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {blockFfmpeg(br);}} catch (IOException e) {System.out.println("字幕增加失败");e.printStackTrace();}return true;}/*** 常用命令** @return*/public static void cmd() {// FIXME: 2023/1/31 还有很多类似命令 不再一一列举 ,附上命令,具体写法参考 getTargetThumbnail或addSubtitle方法// FIXME: 2023/1/31 ffmpeg命令网上搜索即可// 剪切视频// ffmpeg -ss 00:00:00 -t 00:00:30 -i test.mp4 -vcodec copy -acodec copy output.mp4// * -ss 指定从什么时间开始// * -t 指定需要截取多长时间// * -i 指定输入文件// ffmpeg -ss 10 -t 15 -accurate_seek -i test.mp4 -codec copy cut.mp4// ffmpeg -ss 10 -t 15 -accurate_seek -i test.mp4 -codec copy -avoid_negative_ts 1 cut.mp4// 拼接MP4// 第一种方法:// ffmpeg -i "concat:1.mp4|2.mp4|3.mp4" -codec copy out_mp4.mp4// 1.mp4 第一个视频文件的全路径// 2.mp4 第二个视频文件的全路径// 提取视频中的音频// ffmpeg -i input.mp4 -acodec copy -vn output.mp3// -vn: 去掉视频;-acodec: 音频选项, 一般后面加copy表示拷贝// 音视频合成// ffmpeg -y –i input.mp4 –i input.mp3 –vcodec copy –acodec copy output.mp4// -y 覆盖输出文件// 剪切视频// ffmpeg -ss 0:1:30 -t 0:0:20 -i input.mp4 -vcodec copy -acodec copy output.mp4// -ss 开始时间; -t 持续时间// 视频截图// ffmpeg –i test.mp4 –f image2 -t 0.001 -s 320x240 image-%3d.jpg// -s 设置分辨率; -f 强迫采用格式fmt;// 视频分解为图片// ffmpeg –i test.mp4 –r 1 –f image2 image-%3d.jpg// -r 指定截屏频率// 将图片合成视频// ffmpeg -f image2 -i image%d.jpg output.mp4// 视频拼接// ffmpeg -f concat -i filelist.txt -c copy output.mp4// 将视频转为gif// ffmpeg -i input.mp4 -ss 0:0:30 -t 10 -s 320x240 -pix_fmt rgb24 output.gif// -pix_fmt 指定编码// 视频添加水印// ffmpeg -i input.mp4 -i logo.jpg// -filter_complex[0:v][1:v]overlay=main_w-overlay_w-10:main_h-overlay_h-10[out] -map [out] -map// 0:a -codec:a copy output.mp4// main_w-overlay_w-10 视频的宽度-水印的宽度-水印边距;}//=========================================================================================//private static final int WIDTH = 50, HEIGHT = 50;public static String generateTagVideo(String srcVideoPath) {long start = System.currentTimeMillis();String targetVideo = generateTagVideo(srcVideoPath, null);logger.info("generateTagVideo 耗时 {}", (System.currentTimeMillis() - start));logger.info("generateTagVideo srcVideo {} targetVideo {}", srcVideoPath, targetVideo);return targetVideo;}public static String generateTagVideo(String srcVideoPath, String targetVideoPath) {long start = System.currentTimeMillis();//视频文件路径File srcVideoFile = new File(srcVideoPath);if (!srcVideoFile.exists() || !srcVideoFile.isFile()) {logger.info("generateTagVideo srcVideoPath is not video file {}", srcVideoPath);return "";}//图片文件、视频保存跟路径File genRoot = new File(StringUtils.isBlank(targetVideoPath) ? srcVideoFile.getParentFile().getAbsolutePath() : targetVideoPath,StringUtils.replace(srcVideoFile.getName(), ".", "_") + "_gen");String targetFileName = StringUtils.substringBeforeLast(srcVideoFile.getName(), ".") + "_gen" + ".mp4";//判断是否存在已生成的视频文件if (genRoot.exists() && genRoot.isDirectory()) {Optional<File> optionalFile = Arrays.stream(Objects.requireNonNull(genRoot.listFiles())).filter(f -> StringUtils.equals(f.getName(), targetFileName)).findFirst();if (optionalFile.isPresent()) {return optionalFile.get().getAbsolutePath();}}//图片路径File imgDir = new File(genRoot, "img");imgDir.mkdirs();//视频文件拆分成图片splitVideo2Img(srcVideoPath, imgDir.getAbsolutePath());//获取要RPC调用的文件List<File> imgList = getImgList(imgDir);//TODO 跟进图片,比对后,获取图片及坐标信息int x = 1000, y = 500;//TODO 获取需要标注的图片文件集合,及标注坐标// imgList = new ArrayList<>();//替换文件replaceImgFile(imgList, x, y, WIDTH, HEIGHT);//合并图片文件为视频String targetPath = mergeImg2Video(genRoot, targetFileName, imgDir.getAbsolutePath());logger.info("generateTagVideo 耗时:{},src:\t {},\t target:\t{}",(System.currentTimeMillis() - start), srcVideoPath, targetPath);//删除图片文件//deleteFile(imgDir);return targetPath;}private static String mergeImg2Video(File videoPath, String targetFileName, String imgPath) {try (ProcessWrapper ffmpeg = new DefaultFFMPEGLocator().createExecutor()) {ffmpeg.addArgument("-f");ffmpeg.addArgument("image2");ffmpeg.addArgument("-i");ffmpeg.addArgument(imgPath + File.separator + "image_%6d.jpg");String video = videoPath + File.separator + targetFileName;ffmpeg.addArgument(video);ffmpeg.execute();hold(ffmpeg);return video;} catch (Exception e) {logger.error("mergeImg2Video error imgPath {}", imgPath, e);}return "";}public static String splitVideo2Img(String path, String target) {try (ProcessWrapper ffmpeg = new DefaultFFMPEGLocator().createExecutor()) {long start = System.currentTimeMillis();ffmpeg.addArgument("-i");ffmpeg.addArgument(path);ffmpeg.addArgument("-r");ffmpeg.addArgument("10"); //提取图片频率,视频默认一秒25帧,数字越小,视频播放越快ffmpeg.addArgument("-f");ffmpeg.addArgument("image2");ffmpeg.addArgument(target + File.separator + "image_%6d.jpg");ffmpeg.execute();ffmpeg.getProcessExitCode();logger.info("split2Img 耗时:{}", (System.currentTimeMillis() - start));} catch (Exception e) {logger.error("splitVideo2Img error ", e);}return target;}private static void hold(ProcessWrapper ffmpeg) throws Exception {try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {blockFfmpeg(br);}}private static void replaceImgFile(List<File> imgList, int x, int y, int width, int height) {long start = System.currentTimeMillis();logger.info("替换文件开始 {}", start);imgList.stream().map(file ->CompletableFuture.runAsync(() ->drawImg(file, x, y, width, height))).collect(Collectors.toList()).stream().map(CompletableFuture::join).collect(Collectors.toList());logger.info("替换文件结束 ,共耗时:{}", (System.currentTimeMillis() - start));}/*** 获取根目录下的所有文件** @param imgRoot 根目录* @return 所有文件*/private static List<File> getImgList(File imgRoot) {File[] files = imgRoot.listFiles();return Arrays.stream(files).collect(Collectors.toList());}/*** 删除文件** @param file 要删除的文件*/private static void deleteFile(File file) {//删除图片文件Arrays.stream(Objects.requireNonNull(file.listFiles())).map(f -> CompletableFuture.runAsync(f::delete)).collect(Collectors.toList()).stream().map(CompletableFuture::join).collect(Collectors.toList());file.delete();}/*** 对图片文件 画矩形标注** @param file 图片文件* @param x 位置x* @param y 位置y* @param width 标注宽度* @param height 标注高度*/public static void drawImg(File file, int x, int y, int width, int height) {try {BufferedImage image = ImageIO.read(file);Graphics graphics = image.getGraphics();graphics.setColor(Color.RED);graphics.drawRect(x, y, width, height);FileOutputStream outputStream = new FileOutputStream(file);ImageIO.write(image, "jpeg", outputStream);outputStream.close();} catch (Exception e) {logger.error("drawImg error ", e);}}public static void main(String[] args) {String path = "D:\\WorkSpace\\video\\src\\b.mp4";String result = generateTagVideo(path);System.out.println(result);}}
参考
相关文章:
Java使用FFmpeg对视频文件打标记
免安装 FFmpeg <dependency><groupId>ws.schild</groupId><artifactId>jave-all-deps</artifactId><version>3.0.1</version><exclusions><!-- 排除windows 32位系统 --><exclusion><groupId>ws.sch…...
Redux 学习笔记
在使用 React Redux 前,我们首先了解一下 Redux 的一些基础知识。 Redux 是 JavaScript 应用程序中用于状态管理的容器。它不依赖于任何框架,可以与任何 UI 库和框架一起使用。在应用程序中使用 Redux 时,Redux 是以可预测的方式管理状态。 …...
【Bug】8086汇编学习
文章目录 随笔Bug1、masm编译报错:Illegal use of register2、debug中使用段前缀3、[idata]在编译器中的处理4、push立即数报错5、报错:improper operand type6、程序莫名跳转到未知位置 (doing)7、DOSBox失去响应8、程序运行显示乱码9、程序运行导致DOS…...
JetBrains系列IDE全家桶激活
jetbrains全家桶 正版授权,这里有账号授权的渠道: https://www.mano100.cn/thread-1942-1-1.html 附加授权后的一张图片...
洛谷p1618三连击
import java.util.Scanner; //将 1-9 共9个数分成3组,分别组成3个三位数,且使这3个三位数构成A:B:C的比例,试求出所有满足条件的3个三位数。不满足输出“No!!!”。 public class Main {public static void main(String[] args) {Scanner sc …...
微信公众号h5写一个全局调用微信分享功能
1. 首先先安装依赖 npm install weixin-js-sdk --save 2. app.vue文件 <script> export default { onLaunch: function(e) {}, onShow: function(e) { console.log(App Show页面初始); // 路由参数存缓存的 这是为了防止他…...
聊聊精益需求的产生过程
这是鼎叔的第七十八篇原创文章。行业大牛和刚毕业的小白,都可以进来聊聊。 欢迎关注本公众号《敏捷测试转型》,星标收藏,大量原创思考文章陆续推出。本人新书《无测试组织-测试团队的敏捷转型》已出版ÿ…...
Linux - 还不懂 gdb 调试器?(调试软件)
前言 当前,我们可以使用 make/makefile 来程序化执行代码文件;可以使用 gcc/g 等编译器来编译代码;可以使用 vim 编辑器来编写代码;其实在 Linux 当中还有一个工具,可以实现调试工作,这个工具就是 -- gdb。…...
Linux:程序地址空间/虚拟地址等相关概念理解
文章目录 程序地址空间虚拟地址和物理地址地址的转换地址空间是什么? 程序地址空间 在C和C程序中,一直有一个观点是,程序中的各个变量等都会有一定的地址空间,因此才会有诸如取地址,通过地址访问等操作,那…...
Python之爬虫
目录 HTTP请求HTTP响应获得页面响应伪装用户访问打包数据爬取豆瓣top250 HTTP请求 HTTP:HypertextTransferProtcol 超文本传输协议 1、请求行 POST/user/info?new_usertrue HTTP/1.1#资源了路径user/info 查询参数new_usertrue 协议版本HTTP/1.1 2、请求头 Ho…...
打造自己的前端组件库(奶妈版,超详细)
打造自己的前端组件库 demo是开源的,自己上npm 或者 github 上都能搜到 新建vue项目(sass js vue2) vue create yt-ui 修改文件目录(如下) 修改: 1.src 更名 examples; 2. src/components移动到项目最外层;3.vue.config.js更改入口文件 /…...
6.调制阶数相关
1、调制阶数与峰均比的关系 调制阶数(modulation order)对峰均比(有一定的影响。 峰均比是用于衡量调制信号或波形在幅度上的动态范围的指标。它表示信号的最大峰值与平均功率之间的比值。较高的峰均比可能导致信号在传输或放大过程中出现过…...
Maven多模块管理(转载)
注意:父模块需设定打包方式为pom https://cloud.tencent.com/developer/article/1667275 dependencyManagement 统一管理子类依赖版本 在父类maven中加入,不会继承给子类,只能规定子类的依赖版本,子类加入dependence后无需写入 …...
运维学习CentOS 7进行Nightingale二进制部署
.因为Nightingale需要MySQL保存一些数据,所以可以参考《CentOS 7.6使用mysql-8.0.31-1.el7.x86_64.rpm-bundle.tar安装Mysql 8.0》部署MySQL。 https://github.com/ccfos/nightingale/releases是可以github上下载Nightingale二进制安装包。 https://n9e.github.io/…...
安装Docker
本安装教程参考Docker官方文档,地址如下:https://docs.docker.com/engine/install/centos/ 卸载旧版 首先如果系统中已经存在旧的Docker,则先卸载: yum remove docker \ docker-client \ docker-client-latest \ docker-common…...
【uniapp/uView】解决消息提示框悬浮在下拉框之上
需要实现这样的效果,即 toast 消息提示框在 popup 下拉框之上: 解决方法,把 <u-toast ref"uToast" /> 放在 u-popup 里面即可,这样就可以提升 toast 的优先级: <!-- 弹出下拉框 --><u-popu…...
有效管理token,充分发挥ChatGPT的能力
目录 给提供了 Token 的计算工具,来理解一下Token的计算方式,网址如下: 窗口如下: 实际消耗 Token 数量为 59个,换算之后为2.1-2.2的比例,即一个汉字消耗2.12.2个Token, 再测一下英文的Token消耗,包含空格在内,一共52个英文字母,消耗Token 13个,正好对应13个单词,…...
Python —— 验证码的处理执行JavaScript语句
1、验证码的处理 1、概述&绕过验证码的方案 很多的网站都在登录页面加入了识别文字,识别图片,拖动拼图的验证码方式来防止爬虫、恶意注册 等,如果是做自动化,需要绕过验证码才能进入下一步操作,那么有4种方案可以…...
MS12_020 3389远程溢出漏洞
1.search ms12_020 搜索ms12_020 2.use auxiliary/scanner/rdp/ms12_020_check 检查是否存在ms12_020漏洞 show options 查看所需参数 set RHOSTS x.x.x.x 设置目标IP地址 run 执行 检测出来有Ms12_020漏洞 3.use auxiliary/dos/windows/rdp/ms12_020_maxchannelids 选择…...
Pytorch ddp切换forward函数 验证ddp是否生效
DDP及其在pytorch中应用 ddp默认调用forward函数,有些模型无法使用forward函数,可以对模型包装一下。 class modelWraper(nn.Module):def __init__(self, model):super().__init__()self.model modeldef forward(self, *args, **kwargs):return self.…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
