I need to build a unique filename in a multithreaded application which is serializing some data on the disk.
What approach could I use to ensure a unique name.
The app was not multithreaded before and was using Ticks. When using multiple threads, it failed much faster than I expected.
I now added the CurrentThreadId to the filename, and that should do it
string.Format("file_{0}_{1}.xml", DateTime.Now.Ticks, Thread.CurrentThread.ManagedThreadId)
Is there any "smarter" way of doing this?
What about Guid.NewGuid() instead of the thread id?
string.Format("file_{0}_{1:N}.xml", DateTime.Now.Ticks, Guid.NewGuid())
By keeping the ticks as part of the name, the names will still be in approximate date order if ordering is important.
The {1:N} gets rid of the curly braces and dashes in the guid.
Also, consider using DateTime.UtcNow.Ticks, so as to guarantee incremental ticks when daylight saving time kicks in.
Depending on your needs you can try:
System.IO.Path.GetTempFileName();
This creates a uniquely named temporary file in the %temp% directory. I suppose you can copy the file to your target location before or after writing to it.
To actually reserve the filename you will have to create the file immediately and check for exceptions.
Another option would be to include a Guid value in your filename.
How about
Guid.NewGuid().ToString()
Related
This question already has answers here:
How to Generate unique file names in C#
(20 answers)
Closed 4 years ago.
I have this code here that take base 64 string and creates bytes, next I create a file name for these bytes.
byte[] bytes = System.Convert.FromBase64String(landingCells.imageBytes);
var filePath = landingCells.jobNo + DateTime.Now.ToString("yyyyMMddHHmmssffffff");
next I have save these bytes:
System.IO.File.WriteAllBytes("C:/app/Images/" + filePath + ".jpg", bytes);
The problem I am having is I am calling these lines of code in a loop via an iOS app and sometimes the yyyyMMddHHmmssffffff is the same as the previous item in the loop. My question, how can I make the file names more unique so this does not happen.
Try This by using Guid.NewGuid():
var uniquecode=Guid.NewGuid();
var filePath = landingCells.jobNo + DateTime.Now.ToString("yyyyMMddHHmmssffffff")+uniquecode;
Using a date-based name will limit your file creation rate to the frequency of the system clock (and is also not threadsafe) which is why you are seeing duplicate file names when you complete iterations of your loop too quickly. You have several options to make it more unique that depend on what your requirements are:
Add an incrementing counter suffix to the file name when the date is the same as the date of the last file written
Incorporate a GUID into the file name. This will be less readable than the counter suffix but will guarantee uniqueness even across a distributed system and won't require you to maintain a counter.
Incorporate some other original information about the file or its metadata into the name that when combined with the date will be unique
Come up with some custom name generation algorithm that will generate unique names for every (even repeated) input. How you do this depends on the domain you're working within and the data you're dealing with.
I'm not sure what kind app you're building, but it's worth reevaluating whether you actually need to write that many images to disk per second and if you do whether a video would be better. Throttling the writes would probably not be a bad idea and it would also solve the naming problem.
I am testing software in C# and must ensure proper behavior (graceful failure) occurs when a program is given an invalid full path. Initially this is trivial,as I give something like "Q:\\fakepath" and since there is no Q drive mounted on the system, the program fails as expected.
However, I would like my test to be robust and want a way to generate a path that is guaranteed to not exist and to not be able to exist. The path must be full since if it doesn't start with a drive letter it will be treated relative to some directory, resulting in no failure.
Some approaches I have though of are to search for local drives that are mounted and then pick a drive letter that does not appear. This would work fine and I might end up using this, but I would prefer a more elegant solution such as using a drive letter that could not possibly exist.
Another (potential) option is to use invalid characters in the path name. However, using invalid characters is not preferred as it actually results in a different failure mode of the program.
So formally: How can I most elegantly generate a full path that is guaranteed not be invalid?
EDIT: The program I am testing will go ahead and create a directory (including parent directories) if it is on a valid drive but in a location that does not already exist. Hence, this path needs to be something that couldn't be created with something like Directory.CreateDirectory(<path>), not just something that doesn't already exist.
One method would be to use the Windows API to create a temporary folder. This might sound counterintuitive, but now you have a known empty folder, any path you specify inside it is guaranteed to not exist. For example:
//From https://stackoverflow.com/a/278457/1663001:
public string GetTemporaryDirectory()
{
string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(tempDirectory);
return tempDirectory;
}
public string GetNonexistantPath()
{
return Path.Combine(GetTemporaryDirectory(), "no-such-file");
}
One way to get a guaranteed invalid folder path is have a file that exists with the same name as part of the directory path.
public string Example()
{
string filePath = Path.GetTempFileName(); //Creates a uniquely named, zero-byte temporary file on disk.
var invalidDirectoryPath = Path.Combine(filePath, "CanNotExist");
Directory.CreateDirectory(invalidDirectoryPath); //throws a IOException
}
You could try using one of the reserved words, for instance C:\NUL (case-sensitive). Trying to create such directory will cause a DirectoryNotFoundException. More details here.
You can use some really long path (say a thousand characters). Your program won't probably be able to create it as it is invalid.
You can try this approach. Not sure though it would work or not but a worth try.
use path: Q:\asddsafkdjfkasjdfklahsjfhskdjfladjfhsafjklasdjfkashfkajsdfhasdklfjashdfkljasdhfklajdfajsdfklajsfkjasjfhadkfjasflhakldfjashdfklajsdjfhaksldjfahsdkljadfklajfkjlkajfkljagkjklfdjgklajdkfljgskljgklfjskgjfkljdsgkfsdgsfgsdfgsfggsdfgsfdgsgwesdfgdgjgfadfsfgffgfsdghijklm
Don't bother about counting the total number of letters, you can do the same using http://www.lettercount.com/
The trick is the max length of windows folder can be 260.
Though I tried in on Windows 10 and the max length allowed to me is 247.
Source_MAX_Length_Of_Folder_On_Windows
So, this folder is guaranteed to be never found. Cheers :)
Although, I think the most elegant solution is checking the mounted drives and generate a path afterwards that you have already mentioned and decided to keep it as a last option.
I want to name my file after the current time in miliseconds since 1970.
At the moment I just have a counter and increment it after every new file. But when the app restarts the counter goes back to zero and I overwrite the files when I start saving them again.
So I was thinking if I just use the time in seconds or miliseconds then I wont have this problem.
So my question is how to I get the time in miliseconds on windows mobile.
This is what I am currently doing to generate my file names.
string fileName = savedCounter + ".jpg";
You can use Ticks
A single tick represents one hundred nanoseconds or one ten-millionth
of a second. There are 10,000 ticks in a millisecond.
DateTime unixEpoch = new DateTime(1970, 1, 1);
DateTime currentDate = DateTime.Now;
long totalMiliSecond = (currentDate.Ticks - unixEpoch.Ticks) /10000;
Console.WriteLine(totalMiliSecond);
string fileName = string.Concat(totalMiliSecond,".jpg");
Console.WriteLine(fileName);
Are you just using the milliseconds to generate a unique filename? If so, you might be much better using Guid.NewGuid().ToString()
DateTime.UtcNow gives you the current Utc time
new DateTime(1970,1,1,0,0,0,DateTimeKind.Utc) gives you 1970
So you could use:
var savedCounter = Math.Round((DateTime.UtcNow - new DateTime(1970,1,1,0,0,0,DateTimeKind.Utc)).TotalSeconds);
Some alternative naming strategies include:
Given that not many copies of your app were around in 1970, you could probably use a baseline date like new DateTime(2012,1,1,0,0,0)
You could also use a DateTime.ToString format like yyyyMMddhhmmss to achieve a string based on a date - and this might be easier for a human to read (e.g. using the debugger or the isolated storage explorer)
Aside - for performance reasons be aware that you shouldn't create too many files in one directory - http://appangles.com/blogs/mickn/wp7/how-many-files-are-too-many-files-for-windows-phone-7-isolated-storage-performance/ - at some point it makes sense to use a single file instead (e.g. a database)
Either use Time or even better with your current Architecture, save the current Counter into the IsolatedStorageSettings. Its easy to use: http://msdn.microsoft.com/en-us/library/cc221360(v=vs.95).aspx
Even if you use a timestamp to generate the name of the file, if multiple instances of your application can run concurrently, then there's still a chance of conflicts. Regardless of whether or not you use a timestamp, you may want to do something like the following:
Initialize a counter to 0
Generate a name for the file, incorporating the counter into its name.
Try to create the file, opening it for exclusive R/W access and requiring that the file not already exist.
If the file creation failed for some reason, increment the counter and repeat steps 2-4.
In fact, this is most likely what routines like System.IO.Path.GetTempFileName() do.
it will be better to use Guid instead of DateTime.
string fileName = System.Guid.NewGuid() + ".jpg";
I want to automate a program that reads a file, processes it and then write it to a new file. The problem is that a new file comes in every day, and the contents are similar, the input file and output file names will change daily. The file name will be in the following format: SAPHR_Joiners_20110323. As you can see the first part of the name will be constant but the date will be unique...... How would i be able to do this?
Thanks alot guys
If you want to read the latest file in a folder, you could query the created date, using System.IO.File.GetCreationTime
In code:
string myFile =
Directory.GetFiles(#"C:\Temp")
.OrderBy<String, DateTime>(file => File.GetCreationTime(file))
.First();
However, if you know that the file-name will follow a strict naming convention, then it is better to access the file by generating the file name as other answers suggest.
Can't you just generate the filename dynamically in your program, and then open the corresponding file? So something like this:
string filename = "SAPHR_Joiners_" + DateTime.Now.ToString("yyyyMMdd");
string[] filecontents = File.ReadAllLines( filename );
Use a FileSystemWatcher class to look for new incoming files if you want prompt respone, otherwise just locate the file based on a current date. If you have further problems, let us know.
Back the days of VB6 one technique that still is in use this days is the folder monitoring
You keep checking if a folder has files, every x in x minutes, or in your case, every day at XX hours for example.
and you could create a Service from your program and that will insure that it will run every time (as long as the machine is on) :)
Those days, in VB6, we didn't had so much as you have today, so, for watching a folder for specific file types (or anything at all) *.* you can use the System.IO.FIleSystemWatcher (example in that page), and to process the file, just use System.IO.TextReader for example
When I add a picture I want it to create a new random file name because if you add a picture with the same name it will just overwrite.
The is a built-in method Path.GetRandomFileName. It returns a random folder name or file name.
The GetRandomFileName method returns a
cryptographically strong, random
string that can be used as either a
folder name or a file name. Unlike
GetTempFileName, GetRandomFileName
does not create a file. When the
security of your file system is
paramount, this method should be used
instead of GetTempFileName.
If you want to use your extension (e.g. .jpg instead of generated), you could use another helper method Path.ChangeExtension:
string extension = ".jpg";
string fileName = Path.ChangeExtension(
Path.GetRandomFileName(),
extension
);
System.IO.Path.GetRandomFileName gets a file name that is guaranteed to be unique.
As you want to save pictures, you could just use a GUID as the filename:
string filename = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".jpg");
I always do it this way when I need another file extension than .tmp (which files get when you create them via GetTempFileName).
Of course you could create the files via GetTempFileName and then rename them, but then you have to check again if a file with the new name exists...
You could generate a Guid and use that for your file name. Although this would mean that the files are not human readable and have no information as to what the content is.
Name your image using a GUID
For C# you can use: System.Guid.NewGuid().ToString()
You could built it using the current time.
string fileName = DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".png";
The above example will format the current time using year, month, day, hours, minutes, seconds, and fractions of a second. (The fraction of a second can be specified with fewer fs if you want it down to one.).
Advantages:
It will sort automatically by the created time in an alphabetically sorted list. (Like default sorting in Windows Explorer.)
It is human readable and provides useful information about the time it is created.
Disadvantages:
If this is a web application (or other sort of multi-thread process) there is a (small) chance of two files getting same name if generated at the same time. This is not an issue if this is a single-thread EXE.
Perhaps you are looking for GetTempFileName.
I would also go with the GUID solution.
Here some code I would use regularly:
public static string GetRandomFileName(string extension)
{
StringBuffer sb = new StringBuffer(Guid.NewGuid().ToString());
if (!string.IsNullOrEmpty(extensions))
{
sb.Append(".");
sb.Append(extension);
}
return sb.ToString();
}
Would provide you with a fine, reusable solution. Put this into your "collection of my greatest moments" - classlibrary and you are good to go.