Introduction
J-AXE file splitter is a Windows application developed using C# .NET to split file into time interval based on size and total files after splits. I would not ignore the fact that this is some kind of new application. Already there are so many open source projects available to achieve the same purpose. Then the question is why I have spent lots of time to develop this app: allow me to explain the question: whatever application is available as open and/or paid, you will not get the options to split the file based on Time interval or duration. But here you will get this functionality along with other functionality with new flavor; and the main point is that I wanted to develop this using C#. NET and by developing this kind of application I will get a chance to learn something new and even I would utilize my free time into something productive. So I did this and here is the UI of this J-AXE file splitter application.
Fig 1. JAXE File Splitter UI.
You might wonder why this application is named J-AXE? Actually the letter J came from Jawed and AXE is to cut some wooden block. So, I have chosen this application name as J-AXE.
Background
Of course, there was something which triggered me to come up with this ideas/application. Last month, I recorded family videos using my camcorder. After that, I added few songs and effects into the recorded video to make it into a movie so that I could distribute this among family members. Now I wanted to split this file into durations instead of size so that I could make it into different parts, something like Part 1 as 30 minutes and Part 2 as 45 Minutes. I searched here and there for open source but everything I got had functionality to splits file only by size not by the time interval.
Then this requirement triggered some chemical imagination into my mind to come up with some application to fulfill my requirement and here it is an application “J-AXE: A file Splitter”.
Using the Code
J-AXE: A file Splitter Windows application is very handy in use. The whole solution is divided into 2 projects. One project would be User interface and the second project would be nothing but our main logic which is in DLL format. The idea to separate logic from UI and make logic as DLL so that anybody can use it easily without referring to my UI implementation.
Let me give you some pictorial presentation of the whole scenario (Class Diagram):
Fig 2. Class Diagram of JAXE File Splitter with JAXE DLL.
First, I would like to explain the implementation of UI part. Then, I will explain the Logic part.
PART 1: User Interface
The block diagram would be something like this:
Fig 3. Block Digaram of JAXE File Splitter.
On UI User will get the below option to split the file:
- Based on Duration (Time duration in sec/min)
- Size (in Bytes/KB/MB)
- And Number of files
Based on the option selected, we will call the corresponding function to splits the file:
Here is the code to call the corresponding method:
On click of Split Button, we will call the button click event function to get the inputs values like Location of file to splits, Output directory and selected Option.
int tabSelected = tabControlDuration.SelectedIndex;
if (!string.IsNullOrEmpty(textBoxFileSplit.Text))
{
if (!string.IsNullOrEmpty(textBoxOpFileLocation.Text))
{
switch (tabSelected)
{
case 0:
DurationSplit();
break;
case 1:
SizeSplit();
break;
case 2:
NoOfFileSplit();
break;
default:
break;
}
Let’s assume that user has clicked on Duration Tab:
Under this option, user needs to provide the Time interval (like 30 minutes of interval) to split the file along with whether they wants the time interval in Minutes or seconds (By default, it would be in seconds). After providing the desired inputs, user needs to click on Split button. On click of this split button, user can see animation of AXE on UI, like something is under AXE. To show the Animation on UI and splitting of files, I have used the concept of BACKGROUND Worker. So that while splitting of the file going on the UI will not get frozen or hang.
Allow me to explain something about background worker (Source: http://www.albahari.com/threading/part3.aspx).
Background worker is part of System.ComponentModel namespace for managing a worker thread.
BackgroundWorker
is a component that allows you to delegate a long running task to a different thread. It doesn’t stop with that. You can place the component on a Windows Form (it is a non-UI control, so it goes into the component tray). You can register event handlers with it. It takes care of running the long running task in a separate thread while running the task to update control (to report result or progress) in the main event handling thread.
BackgroundWorker
has a RunWorkerCompleted
event that fires after the DoWork
event handler has done its job. On DoWork
, the BackgroundWorker
will call our JAXE DLL to split the file and when it’s done, it will call RunWorkerCompleted
to show notification message to the user through UI and reset all the controls to initial states.
Come to the code part:
If you look at my code, I have used RunWorkerAsync
by passing parameter as object. It means I’m telling BackgroundWorker
to do work asynchronously. Here is the code to perform our task:
private void DurationSplit()
{
var durationIntreval = comboBoxDuration.SelectedIndex;
if (!string.IsNullOrEmpty(textBoxDuration.Text))
{
var durationInSeconds = 0.0;
if (durationIntreval == 1)
durationInSeconds=Convert.ToDouble(textBoxDuration.Text)*60;
else
durationInSeconds=Convert.ToDouble(textBoxDuration.Text);
var setInPuts = new SetInPuts
{
SourceFileLocation = textBoxFileSplit.Text,
TargetFolderLocation = textBoxOpFileLocation.Text,
DurationToSplit =durationInSeconds,
TabSelected=tabControlDuration.SelectedIndex
};
pictureBox1.Visible = true;
buttonSplit.Enabled = false;
backgroundWorkerForSplitFile = new BackgroundWorker();
backgroundWorkerForSplitFile.RunWorkerAsync(setInPuts);
backgroundWorkerForSplitFile.DoWork+=new DoWorkEventHandler
(backgroundWorkerForSplitFile_DoWork);
backgroundWorkerForSplitFile.RunWorkerCompleted+=
new RunWorkerCompletedEventHandler(backgroundWorkerForSplitFile_RunWorkerCompleted);
}
}
Our Dowork
will go here, which will ultimately get called by 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;
}
}
}
On completion, the background worker will call RunWorkerCompleted
to update the user with Notification message and alongwith reset all the controls.
Using background worker, we will call our main logic (JAXE DLL) which will content our splits file logic.
If you see the above piece of code, we have called the method like this:
split.SplitFileBasedOnDuration(inputToPass.SourceFileLocation,
inputToPass.DurationToSplit,inputToPass.TargetFolderLocation);
I have called SplitFileBasedOnDuration
method which is part of our JAXE class that resides in our external DLL, to split the file based on time interval.
To get the duration of input file, I have used open source DLL that is nothing but “DirectShowLib
”. To know more about this, visit this link. After getting the whole duration, I have started splitting the file into time intervals.
To split the file based on duration, I have taken the reference with respect to size of input size. Just have a look at the below code.
var fsDur = new FileStream(inputFile, FileMode.Open, FileAccess.Read);
var totalDuration = 0.0;
GetVideoLength(inputFile, out totalDuration);
Duration of each byte.
var durationOfEachFile = Convert.ToDouble(timeDuration);
var fractionOfTimeDuration = totalDuration / durationOfEachFile;
Size of file corresponding to each byte w.r.t. time duration provided by User:
var sizeOfEachFile = (int)Math.Ceiling((double)fsDur.Length / fractionOfTimeDuration);
Now we will get the Total Numbers of files after splitting the source file.
var numberOfFiles = (int)Math.Ceiling((double)fsDur.Length / sizeOfEachFile);
var baseFileName = Path.GetFileNameWithoutExtension(inputFile);
var extension = Path.GetExtension(inputFile);
Finally, we will start reading the source file byte by byte and will place into output file:
for (var i = 1; i <= numberOfFiles; i++)
{
var outputFile = new FileStream(outPutLocation + "\\"
+ baseFileName + "_" + i.ToString().PadLeft(5, Convert.ToChar("0"))
+ extension, FileMode.Create, FileAccess.Write);
var bytesRead = 0;
var buffer = new byte[sizeOfEachFile];
if ((bytesRead = fsDur.Read(buffer, 0, sizeOfEachFile)) > 0)
outputFile.Write(buffer, 0, bytesRead);
outputFile.Close();
}
The output files name would in this format SourceFilename_Numericvalue.extension
.
The whole logic would be as below:
public void SplitFileBasedOnDuration(string inputFile,
double timeDuration,string outPutLocation)
{
var fsDur = new FileStream(inputFile, FileMode.Open, FileAccess.Read);
var totalDuration = 0.0;
GetVideoLength(inputFile, out totalDuration);
var durationOfEachFile = Convert.ToDouble(timeDuration);
var fractionOfTimeDuration = totalDuration / durationOfEachFile;
var sizeOfEachFile = (int)Math.Ceiling((double)fsDur.Length / fractionOfTimeDuration);
var numberOfFiles = (int)Math.Ceiling((double)fsDur.Length / sizeOfEachFile);
var baseFileName = Path.GetFileNameWithoutExtension(inputFile);
var extension = Path.GetExtension(inputFile);
for (var i = 1; i <= numberOfFiles; i++)
{
var outputFile = new FileStream(outPutLocation +
"\\" + baseFileName + "_" + i.ToString().PadLeft(5, Convert.ToChar("0")) +
extension, FileMode.Create, FileAccess.Write);
var bytesRead = 0;
var buffer = new byte[sizeOfEachFile];
if ((bytesRead = fsDur.Read(buffer, 0, sizeOfEachFile)) > 0)
outputFile.Write(buffer, 0, bytesRead);
outputFile.Close();
}
fsDur.Close();
}
Finally our files would be ready to use after splitting the source file. I have tested this on few extension source files, it works perfectly. But I have not tested this on all types of source files. Might be few files would not work. Please let me know if you find some bugs. I would be happy to hear from all of you.
Points of Interest
The first time I used the concept of background worker and I found it very interesting and useful in Windows application when you don’t want your UI to get frozen while doing some complex/lengthy process. Splitting the file based on duration was also very challenging, somehow I overcome those issues, but still there is some deviation (fraction of seconds) in the time duration of output files.
Points to Remember
BackgroundWorker
uses the thread pool, which means you should never call Abort on a BackgroundWorker
thread.