J-AXE:C# 中的文件分割器
J-AXE 文件分割器是一款使用 C# .NET 开发的 Windows 应用程序,用于根据大小和分割后的总文件数,按时间间隔分割文件。
- 下载 JAXE-DLL - 103.36 KB
- 下载 JAXEFileSplitter 源代码 - 36.05 KB
- 下载 JAXEFileSplitter 安装程序 - 473.21 KB
- 下载 JAXE_DLL 源代码 - 109.56 KB
引言
J-AXE 文件分割器是一款使用 C# .NET 开发的 Windows 应用程序,用于根据大小和分割后的总文件数,按时间间隔分割文件。我不会忽视这是一个新应用程序的事实。市面上已经有太多开源项目可以实现相同的功能。那么问题来了,为什么我花了这么长时间来开发这个应用程序呢?请允许我解释一下:无论现有的应用程序是开源的还是付费的,你都无法获得按时间间隔或持续时间分割文件的选项。但在这里,您将获得此功能以及其他新颖的功能;最重要的一点是,我想使用 C#.NET 来开发它,通过开发此类应用程序,我将有机会学习新知识,甚至可以利用我的业余时间做一些有意义的事情。所以我这样做了,这是这个J-AXE 文件分割器应用程序的用户界面。
你可能会想,为什么这个应用程序叫 J-AXE?实际上,J 来自Jawed,而AXE是砍木头的斧头。所以,我选择了J-AXE这个应用程序名称。
背景
当然,有些事情触发了我产生这个想法/应用程序。上个月,我用摄像机录制了家庭视频。之后,我添加了一些歌曲和效果到录制的视频中,将其制作成一部电影,以便我可以分发给家人。现在我想按时长而不是按大小分割这个文件,这样我就可以将其分成几个部分,比如第一部分 30 分钟,第二部分 45 分钟。我到处搜索开源解决方案,但找到的都只能按大小分割文件,而不能按时间间隔分割。
然后这个需求在我脑海中激起了某种化学反应,我想出了一个应用程序来满足我的需求,它就是“J-AXE:文件分割器”。
Using the Code
J-AXE:文件分割器 Windows 应用程序使用起来非常方便。整个解决方案分为 2 个项目。一个项目是用户界面,第二个项目就是我们的主要逻辑,以 DLL 的形式存在。将逻辑与 UI 分离并将逻辑作为 DLL 的想法是为了让任何人都能轻松使用它,而无需参考我的 UI 实现。
让我给您一些整个场景的图示(类图)
首先,我想解释一下UI 部分的实现。然后,我将解释逻辑部分。
第一部分:用户界面
框图大致是这样的
在 UI 上,用户将获得以下选项来分割文件
- 按时长(秒/分钟)
- 按大小(字节/KB/MB)
- 按文件数量
根据所选选项,我们将调用相应的函数来分割文件
这是调用相应方法的代码
点击“分割”按钮时,我们将调用按钮点击事件函数以获取输入值,如要分割的文件位置、输出目录和选定的选项。
//get the tab selected
int tabSelected = tabControlDuration.SelectedIndex;
//make sure that inputs file and target folder name is provided
if (!string.IsNullOrEmpty(textBoxFileSplit.Text))
{
if (!string.IsNullOrEmpty(textBoxOpFileLocation.Text))
{//get the tab selected for the operation
switch (tabSelected)
{
case 0:
DurationSplit();//call the method to split the file based on duration.
break;
case 1:
SizeSplit();//call the method to split the file based on Size in KB/M/Bytes.
break;
case 2:
NoOfFileSplit();//Call the method to split the the file based on Numbers.
break;
default:
break;
}//end of switch
假设用户点击了“时长”选项卡
在此选项下,用户需要提供时间间隔(例如 30 分钟的间隔),以及他们想要以分钟还是秒为单位的时间间隔(默认情况下为秒)。提供所需的输入后,用户需要点击“分割”按钮。点击此分割按钮后,用户可以在 UI 上看到斧头的动画,好像有什么东西在斧头下。为了在 UI 上显示动画和分割文件,我使用了BACKGROUND WORKER的概念。这样,在分割文件的过程中,UI 不会冻结或卡死。
请允许我解释一下 background worker(来源:http://www.albahari.com/threading/part3.aspx)。
Background worker 是 System.ComponentModel 命名空间的一部分,用于管理工作线程。
BackgroundWorker
是一个组件,它允许您将一个耗时任务委托给另一个线程。它不仅限于此。您可以将该组件放在 Windows 窗体上(它是一个非 UI 控件,所以它会进入组件托盘)。您可以与它注册事件处理程序。它负责在单独的线程中运行耗时任务,同时在主事件处理线程中运行该任务以更新控件(报告结果或进度)。
BackgroundWorker
有一个 RunWorkerCompleted
事件,在 DoWork
事件处理程序完成工作后触发。在 DoWork
中,BackgroundWorker
将调用我们的 JAXE DLL 来分割文件,完成后,它将调用 RunWorkerCompleted
以通过 UI 向用户显示通知消息,并将所有控件重置为初始状态。
来看代码部分
如果您查看我的代码,我使用了 RunWorkerAsync
并传递了 object 作为参数。这意味着我告诉 BackgroundWorker
异步执行工作。这是执行我们任务的代码
//Method to split the file based on Duration.
private void DurationSplit()
{
var durationIntreval = comboBoxDuration.SelectedIndex;
//Verify the Duration value provided by user
if (!string.IsNullOrEmpty(textBoxDuration.Text))
{
//Convert the duration in seconds
var durationInSeconds = 0.0;
if (durationIntreval == 1)
durationInSeconds=Convert.ToDouble(textBoxDuration.Text)*60;
else
durationInSeconds=Convert.ToDouble(textBoxDuration.Text);
//set the values
var setInPuts = new SetInPuts
{
SourceFileLocation = textBoxFileSplit.Text,
TargetFolderLocation = textBoxOpFileLocation.Text,
DurationToSplit =durationInSeconds,
TabSelected=tabControlDuration.SelectedIndex
};
//Enabled the animation
pictureBox1.Visible = true;
//disable the button
buttonSplit.Enabled = false;
//initialize the BackGround worker
backgroundWorkerForSplitFile = new BackgroundWorker();
//run back ground worker in sync by passing parameter as object.
backgroundWorkerForSplitFile.RunWorkerAsync(setInPuts);
//force the BGW to do some work
backgroundWorkerForSplitFile.DoWork+=new DoWorkEventHandler
(backgroundWorkerForSplitFile_DoWork);
//Force the GBW to perform some of the task when previous job done
backgroundWorkerForSplitFile.RunWorkerCompleted+=
new RunWorkerCompletedEventHandler(backgroundWorkerForSplitFile_RunWorkerCompleted);
}//end of if condition
}//end of the method
我们的 Dowork
将在此处,它最终会被 background worker 调用。
private static void backgroundWorkerForSplitFile_DoWork
(object sender, DoWorkEventArgs e)
{
//
var split = new JAXE.Jaxe();
var inputToPass = e.Argument as SetInPuts;
if (inputToPass != null)
{
switch (inputToPass.TabSelected)
{
case 0:
split.SplitFileBasedOnDuration(inputToPass.SourceFileLocation,
inputToPass.DurationToSplit,inputToPass.TargetFolderLocation);
break;
case 1:
split.SplitFileBasedOnSize(inputToPass.SourceFileLocation,
inputToPass.SizeToSplit, inputToPass.TargetFolderLocation);
break;
case 2:
split.SplitFileBasedOnNumberOfFiles(inputToPass.SourceFileLocation,
inputToPass.NumOfFileToSplits, inputToPass.TargetFolderLocation);
break;
default:
break;
}
}
}//end of method
完成时,background worker 将调用 RunWorkerCompleted
来向用户更新通知消息,并重置所有控件。
使用 background worker,我们将调用我们的主要逻辑(JAXE DLL),其中将包含我们的文件分割逻辑。
如果您看到上面的代码片段,我们是这样调用方法的
split.SplitFileBasedOnDuration(inputToPass.SourceFileLocation,
inputToPass.DurationToSplit,inputToPass.TargetFolderLocation);
我调用了 SplitFileBasedOnDuration
方法,它是我们外部 DLL 中 JAXE 类的部分,用于按时间间隔分割文件。
为了获取输入文件的时长,我使用了开源 DLL,即“DirectShowLib
”。要了解更多信息,请访问this link。获取完整时长后,我开始按时间间隔分割文件。
为了按时长分割文件,我根据输入文件大小的比例进行了计算。请看下面的代码。
//Open the file in read mode
var fsDur = new FileStream(inputFile, FileMode.Open, FileAccess.Read);
//variable to store inputs file total length in seconds
var totalDuration = 0.0;
//get the length of file in seconds
GetVideoLength(inputFile, out totalDuration);
每个字节的时长。
//get the duration of each in interval to split
var durationOfEachFile = Convert.ToDouble(timeDuration);
//get the duration of 1 bytes file
var fractionOfTimeDuration = totalDuration / durationOfEachFile;
与用户提供的时长相对应的每个字节的文件大小
//get the size corresponding to duration to split the file
var sizeOfEachFile = (int)Math.Ceiling((double)fsDur.Length / fractionOfTimeDuration);
现在我们将获得分割源文件后的总文件数。
//get the total file to split after comparing time interval with size
var numberOfFiles = (int)Math.Ceiling((double)fsDur.Length / sizeOfEachFile);
//the base file name
var baseFileName = Path.GetFileNameWithoutExtension(inputFile);
//get the extension of the file
var extension = Path.GetExtension(inputFile);
最后,我们将开始逐字节读取源文件并将其放入输出文件
for (var i = 1; i <= numberOfFiles; i++)
{
//file name after splitting
var outputFile = new FileStream(outPutLocation + "\\"
+ baseFileName + "_" + i.ToString().PadLeft(5, Convert.ToChar("0"))
+ extension, FileMode.Create, FileAccess.Write);
var bytesRead = 0;
//array of bytes to hold the piece of file data after splitting
var buffer = new byte[sizeOfEachFile];
//split the file byte by byte
if ((bytesRead = fsDur.Read(buffer, 0, sizeOfEachFile)) > 0)
outputFile.Write(buffer, 0, bytesRead);
outputFile.Close();
}
输出文件的名称格式将是 SourceFilename_Numericvalue.extension
.。
整个逻辑如下
//Split the file based on duration
//Note: There might be a chances where exact duration of the file
//would not be same as user provided. deviation of few seconds
public void SplitFileBasedOnDuration(string inputFile,
double timeDuration,string outPutLocation)
{
//Open the file in read mode
var fsDur = new FileStream(inputFile, FileMode.Open, FileAccess.Read);
//variable to store inputs file total length in seconds
var totalDuration = 0.0;
//get the length of file in seconds
GetVideoLength(inputFile, out totalDuration);
//get the duration of each in interval to split
var durationOfEachFile = Convert.ToDouble(timeDuration);
//get the duration of 1 bytes file
var fractionOfTimeDuration = totalDuration / durationOfEachFile;
//get the size corresponding to duration to split the file
var sizeOfEachFile = (int)Math.Ceiling((double)fsDur.Length / fractionOfTimeDuration);
//get the total file to split after comparing time interval with size
var numberOfFiles = (int)Math.Ceiling((double)fsDur.Length / sizeOfEachFile);
//the base file name
var baseFileName = Path.GetFileNameWithoutExtension(inputFile);
//get the extension of the file
var extension = Path.GetExtension(inputFile);
for (var i = 1; i <= numberOfFiles; i++)
{
//file name after splitting
var outputFile = new FileStream(outPutLocation +
"\\" + baseFileName + "_" + i.ToString().PadLeft(5, Convert.ToChar("0")) +
extension, FileMode.Create, FileAccess.Write);
var bytesRead = 0;
//array of bytes to holed the piece of file data after splitting
var buffer = new byte[sizeOfEachFile];
//split the file byte by byte
if ((bytesRead = fsDur.Read(buffer, 0, sizeOfEachFile)) > 0)
outputFile.Write(buffer, 0, bytesRead);
outputFile.Close();
}
//close the File stream
fsDur.Close();
//return "Done!";
}
最终,在分割完源文件后,我们的文件就可以使用了。我在几种扩展名的源文件上测试了它,效果很好。但我没有在所有类型的源文件上进行测试。可能有些文件无效。如果您发现任何错误,请告知我。我将很高兴收到大家的反馈。
关注点
这是我第一次使用 background worker 的概念,我发现它在 Windows 应用程序中非常有趣且有用,当你不想让 UI 在执行复杂/耗时的过程时冻结。按时长分割文件也极具挑战性,我克服了一些问题,但输出文件的时长仍然存在一些偏差(几分之一秒)。
注意事项
BackgroundWorker
使用线程池,这意味着您永远不应在 BackgroundWorker
线程上调用Abort。