MemoryMappedFiles Could not find a part of the path - c#

I have a few methods which work with MemoryMappedFiles for writing/reading data. They work alright if I use simple string for file name, for example "file.mmf". However if I use full directory path the above mentioned exception is being thrown -
Exception has been thrown by the target of an invocation.
With inner exception - {"Could not find a part of the path."}. Here is how my method looks like:
public void WriteToFile(string fileName, string value)
{
string newFileName = CombineDirectory(fileName);
byte[] newValue = Encoding.UTF8.GetBytes(value);
long capacity = newValue.Length + INT_MAXVALUE_TO_BYTEARRAY_LENGTH;
using (var mmf = MemoryMappedFile.CreateFromFile(newFileName, FileMode.Create, newFileName, capacity))
{
using (var accesor = mmf.CreateViewAccessor())
{
byte[] newValueLength = BitConverter.GetBytes(value.Length);
accesor.WriteArray(0, newValueLength, 0, newValueLength.Length);
accesor.WriteArray(INT_MAXVALUE_TO_BYTEARRAY_LENGTH, newValue, 0, newValue.Length);
}
}
}
My path looks like this :
"C:\\Users\\MyUser\\Documents\\Visual Studio 2012.mmf"
And I am using
Path.Combine
The exception occurs on the first 'using' line. If I try to create a file using the same file path with
File.Create
the file is being created with no problem.
If anyone has any suggestions, that would be great.
Regards

You need to make sure that the mapName argument (i.e. the third argument in your call to CreateFromFile) is not identical to the file path. It will throw a PathNotFound exception if you do. Not really helpful in figuring out why it is failing, I agree.
So your options for choosing a map name value:
Generate some unique key, e.g. Guid.NewGuid().ToString()
Use a constant value, e.g. "MySpecialMapForThings"
Use some convention, e.g. generate a unique key that you also use for just the file name part of the mapped file.
An example for the last option:
public static Tuple<FileInfo, string> GenerateMapInfo(string mapDirectory, string fileExtension)
{
var uniqueMapName = Guid.NewGuid().ToString();
var fileName = Path.Combine(mapDirectory, Path.ChangeExtension(uniqueMapName, fileExtension));
return Tuple.Create(new FileInfo(fileName), uniqueMapName);
}
public void WriteToFile(Tuple<FileInfo, string> mapInfo, string value)
{
byte[] newValue = Encoding.UTF8.GetBytes(value);
long capacity = newValue.Length + INT_MAXVALUE_TO_BYTEARRAY_LENGTH;
using (var mmf = MemoryMappedFile.CreateFromFile(mapInfo.Item1.FullName, FileMode.Create, mapInfo.Item2, capacity))
using (var accesor = mmf.CreateViewAccessor())
{
byte[] newValueLength = BitConverter.GetBytes(value.Length);
accesor.WriteArray(0, newValueLength, 0, newValueLength.Length);
accesor.WriteArray(INT_MAXVALUE_TO_BYTEARRAY_LENGTH, newValue, 0, newValue.Length);
}
}

Related

Reference Embedded Resource from Codedom Compiled Exe

I am using CodeDom Compiler and Microsoft.CSharp, I am trying to embed a resource and call it. The reason I don't try to call properties is because I always get an error saying Properties does not exist in the current context. So I want to know if doing
Parameters.EmbeddedResources.Add("C:/Users/User1/Music/sample.mp3"); is actually helpful or if I should be doing it another way. This is what I have now in the compiler source:
Extract("TestCompiler", "C:/Users/User1/Downloads", "", "Music.mp3");
private static void Extract(string NameSpace, string OutputDir, string InternalPath, string ResourceName){
Assembly assembly = Assembly.GetCallingAssembly();
using (Stream s = assembly.GetManifestResourceStream(NameSpace + "." + (InternalPath == "" ? "" : InternalPath + ".") + ResourceName))
using (BinaryReader r = new BinaryReader(s))
using (FileStream fs = new FileStream(OutputDir + "\\" + ResourceName, FileMode.OpenOrCreate))
using (BinaryWriter w = new BinaryWriter(fs))
w.Write(r.ReadBytes((int)s.Length));
}
When I do this and run the compiled exe this is the exception/error I get:
Unhandled Exception: System.ArgumentNullException: Value cannot be null.
Parameter name: input
at System.IO.BinaryReader..ctor(Stream input, Encoding encoding, Boolean leaveOpen)
at TestCompiler.Program.Extract(String NameSpace, String OutputDir, String InternalPath, String ResourceName)
at TestCompiler.Program.Main(String[] args)
I also have tried doing Extract("TestCompiler", "C:/Users/User1/Downloads", "Resources", "Music.mp3"); but I get the same error.
Is calling a embedded resource possible or should I give up? I've been at this for 3 days.
To answer my own question, I had to get all of the resources by doing this:
string[] Resources = Assembly.GetExecutingAssembly().GetManifestResourceNames();
and to reference and extract them I did this:
foreach(string Resource in Resources)
WriteResources(Resources[Resource], "C:\\Test\\example.mp3");
public static void WriteResources(string Name, string Output){
using(var Resource = Assembly.GetExecutingAssembly().GetManifestResourceStream(Name))
using(var File = new FileStream(Output, FileMode.Create, FileAccess.Write))
Resource.CopyTo(File);
}
Luckily I was able to finish my project after some solid days.

Storing and getting back files from MongoDB

I am working on c# .Net 4.5
I have to upload some files on MongoDB and in other module, I have to get them back based on metadata.
for that I am doing like below,
static void uploadFileToMongoDB(GridFSBucket gridFsBucket)
{
if (Directory.Exists(_sourceFilePath))
{
if (!Directory.Exists(_uploadedFilePath))
Directory.CreateDirectory(_uploadedFilePath);
FileInfo[] sourceFileInfo = new DirectoryInfo(_sourceFilePath).GetFiles();
foreach (FileInfo fileInfo in sourceFileInfo)
{
string filePath = fileInfo.FullName;
string remoteFileName = fileInfo.Name;
string extension = Path.GetExtension(filePath);
double fileCreationDate = fileInfo.CreationTime.ToOADate();
GridFSUploadOptions gridUploadOption = new GridFSUploadOptions
{
Metadata = new BsonDocument
{{ "creationDate", fileCreationDate },
{ "extension", extension }}
};
using (Stream fileStream = File.OpenRead(filePath))
gridFsBucket.UploadFromStream(remoteFileName, fileStream, gridUploadOption);
}
}
}
and downloading,
static void getFileInfoFromMongoDB(GridFSBucket bucket, DateTime startDate, DateTime endDate)
{
double startDateDoube = startDate.ToOADate();
double endDateDouble = endDate.ToOADate();
var filter = Builders<GridFSFileInfo>.Filter.And(
Builders<GridFSFileInfo>.Filter.Gt(x => x.Metadata["creationDate"], startDateDoube),
Builders<GridFSFileInfo>.Filter.Lt(x => x.Metadata["creationDate"], endDateDouble));
IAsyncCursor<GridFSFileInfo> fileInfoList = bucket.Find(filter); //****
if (!Directory.Exists(_destFilePath))
Directory.CreateDirectory(_destFilePath);
foreach (GridFSFileInfo fileInfo in fileInfoList.ToList())
{
string destFile = _destFilePath + "\\" + fileInfo.Filename;
var fileContent = bucket.DownloadAsBytes(fileInfo.Id); //****
File.WriteAllBytes(destFile, fileContent);
}
}
in this code (working but) I have two problems which I am not sure how to fix.
If i have uploaded a file and I upload it again, it actually gets
uploaded. How to prevent it?
Ofcourse both uploaded files have different ObjectId but while uploading a file I will not be knowing that which files are already uploaded. So I want a mechanism which throws an exception if i upload already uploaded file. Is it possible? (I can use combination of filename, created date, etc)
If you have noticed in code, actually i am requesting to database server twice to get one file written on disk, How to do it in one shot?
Note lines of code which I have marked with "//****" comment. First I am querying into database to get fileInfo (GridFSFileInfo). I was expecting that I could get actual content of file from this objects only. But I didnot find any related property or method in that object. so I had to do var fileContent = bucket.DownloadAsBytes(fileInfo.Id); to get content. M I missing something basic here ?

Azure stored file has different MD5 checksum than local file (being same file)

I'm using a could service to upload files to an Azure Storage service, so I want to check the file's integrity using MD5 checksum, so first I get the checksum from a function.
public static string GetMD5HashFromFile(Stream stream)
{
using (var md5 = MD5.Create())
{
return BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", string.Empty);
}
}
for the test file I'm using I'm getting: 1dffc245282f4e0a45a9584fe90f12f2 and I got the same result when I use an online tool like this.
Then I upload the file to Azure and get it from my code like this: (In order to avoid include the validations let's assume the file and directories do exist.)
public bool CompareCheckSum(string fileName, string checksum)
{
this.storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("MyConnectionString"));
this.fileClient = this.storageAccount.CreateCloudFileClient();
this.shareReference = this.fileClient.GetShareReference(CloudStorageFileShareSettings.StorageFileShareName);
this.rootDir = this.shareReference.GetRootDirectoryReference();
this.directoryReference = this.rootDir.GetDirectoryReference("MyDirectory");
this.fileReference = this.directoryReference.GetFileReference(fileName);
Stream stream = new MemoryStream();
this.fileReference.DownloadToStream(stream);
string azureFileCheckSum = GetMD5HashFromFile(stream);
return azureFileCheckSum.ToLower() == checksum.ToLower();
}
I also tried to get the checksum using a different process like this:
public bool CompareCheckSum(string fileName, string checksum)
{
this.storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("MyConnectionString"));
this.fileClient = this.storageAccount.CreateCloudFileClient();
this.shareReference = this.fileClient.GetShareReference(CloudStorageFileShareSettings.StorageFileShareName);
this.rootDir = this.shareReference.GetRootDirectoryReference();
this.directoryReference =
this.rootDir.GetDirectoryReference("MyDirectory");
this.fileReference = this.directoryReference.GetFileReference(fileName);
this.fileReference.FetchAttributes();
string azureFileCheckSum = this.fileReference.Metadata["md5B64"];
return azureFileCheckSum.ToLower() == checksum.ToLower();
}
Finally, for the azureFileCheckSum I'm getting: d41d8cd98f00b204e9800998ecf8427e not sure if am I doing something wrong or if something change when I upload the file to the ftp...
Before you call md5.ComputeHash(stream), you need to reset the stream's position to the beginning.
stream.Position = 0;
Of course, this will fail with a NotSupportedException if the stream type doesn't support seeking, but in your case it should work.

How to read a file which build action is marked as embedded/resource?

I have C# project, I added several html files to the solution and marked their action as "resource" or "embedded resource" ("or" -- because I don't see difference when testing).
Now I would like to read them.
When I run this code:
Assembly.GetExecutingAssembly().GetManifestResourceNames()
I get 3 resource names in result:
MyApp.g.resources
MyApp.Properties.Resources.resources
MyApp.Resource.resources
I accessed first, I found desired file name, and I read it this way:
private static byte[] getResource(string name)
{
var assembly = Assembly.GetExecutingAssembly();
string resName = assembly.GetName().Name + ".g.resources";
using (var stream = assembly.GetManifestResourceStream(resName))
{
using (var reader = new System.Resources.ResourceReader(stream))
{
string res_type;
byte[] data;
reader.GetResourceData(name,out res_type,out data);
return data;
}
}
}
As the result I get almost what I wanted -- the content of the file, however with some garbage bytes at the start.
How to do this correctly?
Update: the garbage sequence is -- 6, 0, 0, 239, 187, 191. Three last bytes make BOM.

Edit an argument with a loop

I am attempting to alter a series of 4 .bat files. When I run the program, it prompts me for an input and then writes it to the .bat file.
I took the code below from the microsoft documentation on File.Openwrite, then added some variables to point to the files.
As opposed to copy/pasting the code that actually writes the text, I put a for loop around it with the intent of altering the argument so that the File.OpenWrite piece will look to a different variable (and so a different path/directory) during each iteration. I confirmed that the loop works (if I enter one of the path# variables it will iterate through and write to that file 4 times) and that File.OpenWrite is seeing the correct text each iteration. My only guess is that it is looking at the 'path#' argument literally and not seeing it as a variable. Can someone help me understand how I can alter this argument through iteration?
using System;
using System.IO;
using System.Text;
class Test
{
public static void Main()
{
string path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
string path0 = path + #"\down_fa.bat";
string path1 = path + #"\down_ng.bat";
string path2 = path + #"\down_os.bat";
string path3 = path + #"\down_sp.bat";
string portinput = Console.ReadLine();
string dotbatinput = "DDL -p" + portinput;
// Open the stream and write to it.
for (int i = 0; i < 4; i++)
{
using (FileStream fs = File.OpenWrite("path" + i))
{
Byte[] info =
new UTF8Encoding(true).GetBytes(dotbatinput);
// Add some information to the file.
fs.Write(info, 0, info.Length);
}
}
}
}
You cannot refer to a variable declared in your code using a string and concatenating a number. In this way you pass a literal string to the OpenWrite method not the content of the variable with the name equals to your string.
A simpler approach is to add every batch file to a list of strings and then loop over that list writing the content required
string path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
List<string> batFiles = new List<string>();
batFiles.Add(System.IO.Path.Combine(path, "down_fa.bat"));
batFiles.Add(System.IO.Path.Combine(path, "down_ng.bat"));
batFiles.Add(System.IO.Path.Combine(path, "down_os.bat"));
batFiles.Add(System.IO.Path.Combine(path, "down_sp.bat"));
string portinput = Console.ReadLine();
string dotbatinput = "DDL -p" + portinput;
foreach(string batFile in batFiles)
{
using (FileStream fs = File.OpenWrite(batFile))
{
-----
}
}
File.OpenWrite("path" + 0) != File.OpenWrite(path0)
The left side opens a stream to a file called "path0" which you will find in the bin\Debug directory of your project and the right example writes a file at the location specified in the string path0. The same of course applies to the other numbers. A possible solution would be to use an array or a list:
string[] paths = new string[4].Select(x => System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)).ToArray();
string[0] += ...;
string[1] += ...;
string[2] += ...;
string[3] += ...;
foreach (string path in paths)
{
using (FileStream fs = File.OpenWrite(path))
{
// do stuff
}
}

Categories