直接贴代码了:

ffmpegTest02.cs

    public partial class ffmpegTest02 : FormBase
{
private static readonly string TaskffmpegNETExeFullPath = ConfigurationManager.AppSettings["TaskffmpegNETExeFullPath"]; string _videoFileFullPath = @"D:\Workspace\TestVideo.mp4"; string _othereVideoFileFullPath = @"D:\Workspace\TestVideo{0}.mp4"; string snapshotParentDir = @"D:\Workspace\TestVideoSnapshot"; int exeMoreCrawlSnapshotCount = ; public ffmpegTest02()
{
base.InitForm();
InitializeComponent();
base.InitControls(this.listInfoLog);
} private void OnProgress(object sender, ConversionProgressEventArgs e)
{
ShowAndLog(string.Format("[{0} => {1}]", e.Input.FileInfo.Name, e.Output?.FileInfo.Name), false, null);
ShowAndLog(string.Format("Bitrate: {0}", e.Bitrate), false, null);
ShowAndLog(string.Format("Fps: {0}", e.Fps), false, null);
ShowAndLog(string.Format("Frame: {0}", e.Frame), false, null);
ShowAndLog(string.Format("ProcessedDuration: {0}", e.ProcessedDuration), false, null);
ShowAndLog(string.Format("Size: {0} kb", e.SizeKb), false, null);
ShowAndLog(string.Format("TotalDuration: {0}\n", e.TotalDuration), false, null);
} /// <summary>
/// 此事件短时间(比如:1秒以内)会调用多次
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnData(object sender, ConversionDataEventArgs e)
{
ShowAndLog(string.Format("[从源文件 {0} 到目标文件 {1}],数据 {2}", e.Input.FileInfo.Name, e.Output?.FileInfo.Name, e.Data),
false,
null);
} /// <summary>
/// 此事件短时间(比如:1秒以内)会调用多次
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnDataSimpleShow(object sender, ConversionDataEventArgs e)
{
ShowAndLog(string.Format("[从源文件 {0} 到目标文件 {1}]。", e.Input.FileInfo.Name, e.Output?.FileInfo.Name),
false,
null);
} private void OnComplete(object sender, ConversionCompleteEventArgs e)
{
ShowAndLog(string.Format("从 {0} 到 {1} 处理完成。 ", e.Input.FileInfo.FullName, e.Output?.FileInfo.FullName),
false,
null);
} private void OnError(object sender, ConversionErrorEventArgs e)
{
ShowAndLog(string.Format("[{0} => {1}]: 错误: {2}\n{3}", e.Input.FileInfo.Name, e.Output?.FileInfo.Name, e.Exception.ExitCode, e.Exception.InnerException),
false,
null);
} private IEnumerable<TimeSpan> CreateTimeSpanArray()
{
for (int i = ; i < ; i++)
{
yield return new TimeSpan(, i, );
}
} private ConversionOptions GetConversionOptions(TimeSpan tsItem)
{
return new ConversionOptions
{
Seek = tsItem,
//下面表示3行代码表示需要自定义截图的尺寸
VideoSize = VideoSize.Custom,
CustomWidth = , // 截图的宽度
CustomHeight = , // 截图的高度
FFmpegDrawTextArgs = string.Format("borderw=10:bordercolor=white:fontcolor=#A9A9A8:fontsize=100:fontfile=FreeSerif.ttf:text='{0}\\:{1}\\:{2}':x =(w-text_w-50):y=(h-text_h-50)",
tsItem.Hours.ToString().PadLeft(, ''),
tsItem.Minutes.ToString().PadLeft(, ''),
tsItem.Seconds.ToString().PadLeft(, '')
),
ExtraFFmpegArgs = " -y " // -y 表示目标文件已经存在,则覆盖输出文件
};
} private async void btnStartConvertAndSnapshot_Click(object sender, EventArgs e)
{
string mkvOutputFileFullPath = string.Format(@"D:\Workspace\TestVideo-{0}.mkv", DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
string mkvThumbOutputFileFullPath = string.Format(@"{0}\TestVideo-mkv-thumb-{1}.png", snapshotParentDir, DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
try
{
var inputFile = new MediaFile(_videoFileFullPath);
var outputFile = new MediaFile(mkvOutputFileFullPath);
var thumbNailFile = new MediaFile(mkvThumbOutputFileFullPath); var ffmpeg = new Engine(TaskffmpegNETExeFullPath);
//ffmpeg.Progress += OnProgress;
//ffmpeg.Data += OnDataSimpleShow;
ffmpeg.Error += OnError;
ffmpeg.Complete += OnComplete;
var output = await ffmpeg.ConvertAsync(inputFile, outputFile);
var thumbNail = await ffmpeg.GetThumbnailAsync(output, thumbNailFile, GetConversionOptions(TimeSpan.FromSeconds()));
var metadata = await ffmpeg.GetMetaDataAsync(output);
Console.WriteLine(metadata.FileInfo.FullName);
Console.WriteLine(metadata);
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
} private async void btnGetVideoInfo_Click(object sender, EventArgs e)
{
ShowAndLog("ffmpeg 准备开始获取,请稍后...", false, null);
Stopwatch globalWatch = Stopwatch.StartNew();
var ffmpeg = new Engine(TaskffmpegNETExeFullPath);
//ffmpeg.Progress += OnProgress;
//ffmpeg.Data += OnDataSimpleShow;
ffmpeg.Error += OnError;
ffmpeg.Complete += OnComplete; var inputFile = new MediaFile(_videoFileFullPath);
MetaData md = await ffmpeg.GetMetaDataAsync(inputFile); ShowAndLog(string.Format("Duration:{0}", md.Duration), false, null);
ShowAndLog(string.Format("VideoData:{0}", JsonHelper.SerializeToJson(md.VideoData)), false, null);
ShowAndLog(string.Format("AudioData:{0}", JsonHelper.SerializeToJson(md.AudioData)), false, null); globalWatch.Stop();
ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null); } private async void btnStartSingleSnapshot_Click(object sender, EventArgs e)
{
ShowAndLog("ffmpeg 准备开始转换,请稍后...", false, null);
Stopwatch globalWatch = Stopwatch.StartNew();
IEnumerable<TimeSpan> timeSpanArray = CreateTimeSpanArray();
var ffmpeg = new Engine(TaskffmpegNETExeFullPath);
//ffmpeg.Progress += OnProgress;
//ffmpeg.Data += OnDataSimpleShow;
ffmpeg.Error += OnError;
ffmpeg.Complete += OnComplete; var inputFile = new MediaFile(_videoFileFullPath); int i = ;
string mkvThumbOutputFileFullPath;
List<string> allThumbFullName = new List<string>();
foreach (TimeSpan tsItem in timeSpanArray)
{
i++;
mkvThumbOutputFileFullPath = string.Format(@"{0}\TestVideo-mp4-thumb-{1}-{2}.jpg", snapshotParentDir, DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss.fff"), i);
allThumbFullName.Add(mkvThumbOutputFileFullPath);
var thumbNailFile = new MediaFile(mkvThumbOutputFileFullPath);
await ffmpeg.GetThumbnailAsync(inputFile, thumbNailFile, GetConversionOptions(tsItem) );
}
ShowAndLog("ffmpeg 转换完成。结果如下:", false, null);
globalWatch.Stop();
ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null);
} private async void btnParelleSyncStartMoreCrawlSnapshot_ClickAsync(object sender, EventArgs e)
{
ShowAndLog("准备开始同步多个 TestVideo{n}.mp4 上截图,请稍后...", false, null);
Stopwatch globalWatch = Stopwatch.StartNew(); for (int i = ; i < exeMoreCrawlSnapshotCount; i++)
{
await CreateSnapshotCoreAsync(i + );
}
ShowAndLog("同步多个 TestVideo{n}.mp4 上截图完成。结果如下:", false, null);
globalWatch.Stop();
ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null);
} private async Task CreateSnapshotCoreAsync(int fileId)
{
IEnumerable<TimeSpan> timeSpanArray = CreateTimeSpanArray();
var ffmpeg = new Engine(TaskffmpegNETExeFullPath);
//ffmpeg.Progress += OnProgress;
//ffmpeg.Data += OnDataSimpleShow;
ffmpeg.Error += OnError;
ffmpeg.Complete += OnComplete; var inputFile = new MediaFile(string.Format(_othereVideoFileFullPath, fileId)); //_videoFileFullPath int i = ;
string mkvThumbOutputFileFullPath;
List<string> allThumbFullName = new List<string>();
foreach (TimeSpan tsItem in timeSpanArray)
{
i++;
mkvThumbOutputFileFullPath = string.Format(@"{0}\TestVideo{1}-mp4-thumb-{2}-{3}.jpg", snapshotParentDir, fileId, DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss.fff"), i);
allThumbFullName.Add(mkvThumbOutputFileFullPath);
var thumbNailFile = new MediaFile(mkvThumbOutputFileFullPath);
await ffmpeg.GetThumbnailAsync(inputFile, thumbNailFile, GetConversionOptions(tsItem));
}
} private async void btnParelleAsyncStartMoreCrawlSnapshot_ClickAsync(object sender, EventArgs e)
{
ShowAndLog("准备开始异步并行多个 TestVideo{n}.mp4 上截图,请稍后...", false, null);
Stopwatch globalWatch = Stopwatch.StartNew(); var tasks = Enumerable.Range(, exeMoreCrawlSnapshotCount).Select(i =>
{
return Task.Run(async () =>
{
await CreateSnapshotCoreAsync(i + );
});
});
await Task.WhenAll(tasks);
ShowAndLog("异步并行多个 TestVideo{n}.mp4 上截图完成。结果如下:", false, null);
globalWatch.Stop();
ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null);
} private async void btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop_Click(object sender, EventArgs e)
{
ShowAndLog("准备开始异步并行多个 TestVideo{n}.mp4 上截图(限制最大异步处理单元),请稍后...", false, null);
Stopwatch globalWatch = Stopwatch.StartNew();
var tasks = Enumerable.Range(, exeMoreCrawlSnapshotCount).ParallelForEachAsync(i =>
{
return Task.Run(async () =>
{
await CreateSnapshotCoreAsync(i + );
});
}, Environment.ProcessorCount);
await Task.WhenAll(tasks);
ShowAndLog("异步并行多个 TestVideo{n}.mp4 上截图(限制最大异步处理单元)完成。结果如下:", false, null);
globalWatch.Stop();
ShowAndLog(string.Format("运行结束!共耗时 {0} 毫秒。", globalWatch.ElapsedMilliseconds), false, null);
}
}

ffmpegTest02.Designer.cs

    partial class ffmpegTest02
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null; /// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
} #region Windows Form Designer generated code /// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnStartConvertAndSnapshot = new System.Windows.Forms.Button();
this.listInfoLog = new System.Windows.Forms.ListBox();
this.btnStartSingleSnapshot = new System.Windows.Forms.Button();
this.btnGetVideoInfo = new System.Windows.Forms.Button();
this.btnParelleSyncStartMoreCrawlSnapshot = new System.Windows.Forms.Button();
this.btnParelleAsyncStartMoreCrawlSnapshot = new System.Windows.Forms.Button();
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// btnStartConvertAndSnapshot
//
this.btnStartConvertAndSnapshot.Location = new System.Drawing.Point(, );
this.btnStartConvertAndSnapshot.Name = "btnStartConvertAndSnapshot";
this.btnStartConvertAndSnapshot.Size = new System.Drawing.Size(, );
this.btnStartConvertAndSnapshot.TabIndex = ;
this.btnStartConvertAndSnapshot.Text = "1. 开始先把 TestVideo.mp4 转换 mkv,再在第 1 分钟的时候截图";
this.btnStartConvertAndSnapshot.UseVisualStyleBackColor = true;
this.btnStartConvertAndSnapshot.Click += new System.EventHandler(this.btnStartConvertAndSnapshot_Click);
//
// listInfoLog
//
this.listInfoLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.listInfoLog.Font = new System.Drawing.Font("宋体", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)()));
this.listInfoLog.FormattingEnabled = true;
this.listInfoLog.HorizontalScrollbar = true;
this.listInfoLog.Location = new System.Drawing.Point(, );
this.listInfoLog.Name = "listInfoLog";
this.listInfoLog.ScrollAlwaysVisible = true;
this.listInfoLog.Size = new System.Drawing.Size(, );
this.listInfoLog.TabIndex = ;
//
// btnStartSingleSnapshot
//
this.btnStartSingleSnapshot.Location = new System.Drawing.Point(, );
this.btnStartSingleSnapshot.Name = "btnStartSingleSnapshot";
this.btnStartSingleSnapshot.Size = new System.Drawing.Size(, );
this.btnStartSingleSnapshot.TabIndex = ;
this.btnStartSingleSnapshot.Text = "2. 不转码,直接在第 1分钟的时候从 TestVideo.mp4 上截图";
this.btnStartSingleSnapshot.UseVisualStyleBackColor = true;
this.btnStartSingleSnapshot.Click += new System.EventHandler(this.btnStartSingleSnapshot_Click);
//
// btnGetVideoInfo
//
this.btnGetVideoInfo.Location = new System.Drawing.Point(, );
this.btnGetVideoInfo.Name = "btnGetVideoInfo";
this.btnGetVideoInfo.Size = new System.Drawing.Size(, );
this.btnGetVideoInfo.TabIndex = ;
this.btnGetVideoInfo.Text = "3. 仅仅获取 TestVideo.mp4 视频的信息";
this.btnGetVideoInfo.UseVisualStyleBackColor = true;
this.btnGetVideoInfo.Click += new System.EventHandler(this.btnGetVideoInfo_Click);
//
// btnParelleSyncStartMoreCrawlSnapshot
//
this.btnParelleSyncStartMoreCrawlSnapshot.Location = new System.Drawing.Point(, );
this.btnParelleSyncStartMoreCrawlSnapshot.Name = "btnParelleSyncStartMoreCrawlSnapshot";
this.btnParelleSyncStartMoreCrawlSnapshot.Size = new System.Drawing.Size(, );
this.btnParelleSyncStartMoreCrawlSnapshot.TabIndex = ;
this.btnParelleSyncStartMoreCrawlSnapshot.Text = "4. 同步多个 TestVideo{n}.mp4 上截图";
this.btnParelleSyncStartMoreCrawlSnapshot.UseVisualStyleBackColor = true;
this.btnParelleSyncStartMoreCrawlSnapshot.Click += new System.EventHandler(this.btnParelleSyncStartMoreCrawlSnapshot_ClickAsync);
//
// btnParelleAsyncStartMoreCrawlSnapshot
//
this.btnParelleAsyncStartMoreCrawlSnapshot.Location = new System.Drawing.Point(, );
this.btnParelleAsyncStartMoreCrawlSnapshot.Name = "btnParelleAsyncStartMoreCrawlSnapshot";
this.btnParelleAsyncStartMoreCrawlSnapshot.Size = new System.Drawing.Size(, );
this.btnParelleAsyncStartMoreCrawlSnapshot.TabIndex = ;
this.btnParelleAsyncStartMoreCrawlSnapshot.Text = "5. 异步并行多个 TestVideo{n}.mp4 上截图";
this.btnParelleAsyncStartMoreCrawlSnapshot.UseVisualStyleBackColor = true;
this.btnParelleAsyncStartMoreCrawlSnapshot.Click += new System.EventHandler(this.btnParelleAsyncStartMoreCrawlSnapshot_ClickAsync);
//
// btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop
//
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Location = new System.Drawing.Point(, );
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Name = "btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop";
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Size = new System.Drawing.Size(, );
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.TabIndex = ;
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Text = "6. 异步并行多个 TestVideo{n}.mp4 上截图(限制最大异步处理单元)";
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.UseVisualStyleBackColor = true;
this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop.Click += new System.EventHandler(this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop_Click);
//
// ffmpegTest02
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(, );
this.Controls.Add(this.btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop);
this.Controls.Add(this.btnParelleAsyncStartMoreCrawlSnapshot);
this.Controls.Add(this.btnParelleSyncStartMoreCrawlSnapshot);
this.Controls.Add(this.btnGetVideoInfo);
this.Controls.Add(this.btnStartSingleSnapshot);
this.Controls.Add(this.listInfoLog);
this.Controls.Add(this.btnStartConvertAndSnapshot);
this.Name = "ffmpegTest02";
this.Text = "ffmpegTest02";
this.ResumeLayout(false); } #endregion private System.Windows.Forms.Button btnStartConvertAndSnapshot;
private System.Windows.Forms.ListBox listInfoLog;
private System.Windows.Forms.Button btnStartSingleSnapshot;
private System.Windows.Forms.Button btnGetVideoInfo;
private System.Windows.Forms.Button btnParelleSyncStartMoreCrawlSnapshot;
private System.Windows.Forms.Button btnParelleAsyncStartMoreCrawlSnapshot;
private System.Windows.Forms.Button btnParelleAsyncStartMoreCrawlSnapshotLimitMaxDop;
}

运行结果

图 01

图 02

图 03

图 04

图 05

图 06

图 07

图 08

谢谢浏览!

如何让 FFmpeg 支持异步并行转码、截图等等操作?的更多相关文章

  1. Java Web 中使用ffmpeg实现视频转码、视频截图

    Java Web 中使用ffmpeg实现视频转码.视频截图 转载自:[ http://www.cnblogs.com/dennisit/archive/2013/02/16/2913287.html  ...

  2. windows下使用ffmpeg进行视频转换和截图。

    author:fanfq(xiaoban) Email:fangqing.fan#gmail.comlink:http://fanfq.iteye.com/admin/blogs/655569chan ...

  3. C# 使用 ffmpeg 进行音频转码

    先放一下 ffmpeg 的官方文档以及下载地址: 官方文档:http://ffmpeg.org/ffmpeg.html 下载地址:http://ffmpeg.org/download.html 用 f ...

  4. FFmpeg:视频转码、剪切、合并、播放速调整

    原文:https://fzheng.me/2016/01/08/ffmpeg/ FFmpeg:视频转码.剪切.合并.播放速调整 2016-01-08 前阵子帮导师处理项目 ppt,因为插入视频的格式问 ...

  5. 使用ffmpeg.exe进行转码参数说明

    使用ffmpeg.exe进行转码参数说明 摘自:https://blog.csdn.net/coloriy/article/details/47337641 2015年08月07日 13:04:32  ...

  6. ubuntu系统下安装gstreamer的ffmpeg支持

    当您在安装gstreamer到您的ubuntu系统中时,为了更好地进行流媒体开发,需要安装ffmpeg支持,但一般情况下,直接使用 sudo apt-get install gstreamer0.10 ...

  7. [IBM][CLI Driver] SQL0270N 函数不受支持(原因码:"75")。 SQLSTATE=42997

    db2 update dbm cfg  using FEDERATED  yes 与 自动维护 (AUTO_MAINT) = ON 自动数据库备份 (AUTO_DB_BACKUP) = OFF 自动表 ...

  8. Python3选择支持非ASCII码标识符的缘由

    原文在: PEP 3131 -- Supporting Non-ASCII Identifiers. Python2并不支持非ASCII码标识符. PEP的全称是Python Enhancement ...

  9. API Studio 5.1.2 版本更新:加入全局搜索、支持批量测试API测试用例、读取代码注解生成文档支持Github与码云等

    最近在EOLINKER的开发任务繁重,许久在博客园没有更新产品动态了,经过这些日子,EOLINKER又有了长足的进步,增加了更多易用的功能,比如加入全局搜索.支持批量测试API测试用例.读取代码注解生 ...

随机推荐

  1. 一次业务网关用ASP.NET Core 2.1重构的小结

    目录 前言 统一鉴权 服务限流 路由转发 参数重组 链路跟踪 熔断降级 服务计次 业务指标监控 日志记录 迭代更新 总结 前言 对于API网关,业界貌似对它进行下划分,有下面几个分类/场景. 面向We ...

  2. java 类内部定义接口

    java类内部可以定义接口,作用可以看作是对类功能的进一步补充,类里面包含两部分:一部分是自己的固定的,一部分是可以变化的,而这可变的部分就编程了一个接口. 另一个作用是避免命名冲突. 示例 类Fru ...

  3. Redis for OPS 05:哨兵HA Sentinel

    写在前面的话 上一节的主从环境能够解决我们保证数据安全性的问题,但是却无法解决我们在主节点挂掉的时候服务继续使用的问题,同时也不能自动切换新的主. 我们运维的目的肯定是希望即使主库挂掉一个,服务依旧能 ...

  4. ABAP ALV显示前排序合并及布局显示

    有时候会有用户要求显示出来的ALV立即就是升序或者降序,或者是上下同一个字段值一样的情况显示一次,如 变为 这个时候内表用SORT有时候会不好用,可以使用函数 REUSE_ALV_GRID_DISPL ...

  5. C# - WinFrm应用程序MessageBox自动关闭小实验

    概述 在程序中MessageBox弹出的对话框,用于向用户展示消息,这是一个模式窗口,可阻止应用程序中的其他操作,直到用户将其关闭.但是有时候在自动化程序中,如果弹出对话框,程序将会中断,等待人工的干 ...

  6. Python 情人节超强技能 导出微信聊天记录生成词云

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: Python实用宝典 PS:如有需要Python学习资料的小伙伴可 ...

  7. centOS服务器基本命令

    1.卸载/安装mySQL:(因为我是该服务器的管理员,所以这些命令都不用在前面加sudo) yum remove mysqlyum install mysql 2.进入根目录 cd / 3.查看cen ...

  8. 敏捷软件开发_UML<一>

    敏捷软件开发_UML  所看书籍是:敏捷软件开发_原则.模式与实践_C#版(美)马丁著,这本书写的非常棒,感谢作者.该归纳总结的过程按照我读的顺序写. UML  在建造桥梁,零件,自动化设备之前需要建 ...

  9. Tomcat安装、使用(Windows)

    一.下载.安装 1.下载 进官网下载 : https://tomcat.apache.org/ 选择自己适合的版本.在这里演示的是下载 Tomcat 7(解压安装版). 2.解压.启动tomcat 解 ...

  10. 自定义Vue组件打包、发布到npm以及使用

    本文将帮助:将自己写的Vue组件打包到npm进行代码托管,以及正常发布之后如何使用自己的组件. 本文讲述的仅仅是最基础的实现,其他复杂的操作需要非常熟悉webpack的相关知识,作者将继续学习. 先附 ...