Going from a File Path to using a Resource - c#

My current project uses a direct file path of this excel document to read information off of the excel file. I need to get my project ready for release so I cannot have the project hard code a file path in the form of a string.
I want to embed the Excel File in my resource, which I have done, but know how can I get the file path from Resource, and send a file path to the class which reads the Excel file. The class must be feed a filepath so I was thinking of making a copy of this Excel file, and in the Temp folder then referenceing the file path for the class to read the Excel file.
FileName = #"D:\SomeFolder\ExcelFile.xlsx"; //This is the old code, hard coded
//I need code that is going to make a copy of this file from the Resources and save it somewhere in a temp folder, but then give me
the File path in the form of a string.
string FileName;
// I need the file name to have the directory of this excel that is in the Resource folder
//Call Class to Create XML File and store Data from BIN File Locally on Program
ReadExcel_CreateXML = new ExcelRecorder(FileName);

Something else to think about is that you are probably reading the current files using a FileStream and either a BinaryReader or StreamReader. If that's the case, the consumer of the file could be written to accept an arbitrary Stream instead and then you can create a MemoryStream to pass to the consuming class:
// The resource will be a byte array, I'm just creating a
// byte array manually for example purposes.
var fileData = System.Text.Encoding.UTF8.GetBytes("Hello\nWorld!");
using (var memoryStream = new MemoryStream(fileData))
using (var streamReader = new StreamReader(memoryStream))
{
// Do whatever you need with the file's contents
Console.WriteLine(streamReader.ReadLine());
Console.WriteLine(streamReader.ReadLine());
}
This approach means you won't be cluttering up the client computer with temporary files that you'll need to clean up. It also means your consuming class will become more flexible if you ever need to process data over any other type of Stream.

I'm not sure if this the best solution, but it will work:
1st get the byte[] array of the file in your resources:
byte[] fileByteArray = global::YourProjectNameSpace.Properties.Resources.ExcelFileName
2nd Export the file to a temporary location using this function:
(I got from here: Write bytes to file)
public bool ByteArrayToFile(string _FileName, byte[] _ByteArray)
{
try
{
// Open file for reading
System.IO.FileStream _FileStream =
new System.IO.FileStream(_FileName, System.IO.FileMode.Create,
System.IO.FileAccess.Write);
// Writes a block of bytes to this stream using data from
// a byte array.
_FileStream.Write(_ByteArray, 0, _ByteArray.Length);
// close file stream
_FileStream.Close();
return true;
}
catch (Exception _Exception)
{
// Error
Console.WriteLine("Exception caught in process: {0}",
_Exception.ToString());
}
// error occured, return false
return false;
}
And last access that temporary file like you normally would
Use:
Just create a button in a form and put this code in the button's click event
private void button1_Click(object sender, EventArgs e)
{
byte[] fileByteArray = global::YourProjectNameSpace.Properties.Resources.ExcelFileName;
if (ByteArrayToFile(#"C:\Temp\file.xlsx", fileByteArray))
{
//File was saved properly
}
else
{
//There was an error saving the file
}
}
Hope it works

Related

How to get content of excel's temporary file?

When I open an excel file, a hidden temporary file is generated in the same folder. I can open it with the TotalCommander Viewer, but I always get an IO exception when trying to open with powershell or c#.
new FileStream(#"D:\~$test.xlsx", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
System.IO.IOException: 'The process cannot access the file 'D:~$test.xlsx' because it is being used by another process.'
So how can I get the content?
Unfortunately for some reason you can not open the direct file, so I suggest another method when you copy a the file to a temp file, then read it and finally you delete the temp file, this way you can read it, I suppose TotalCommander uses the same method for opening files in Viewer.
static void Main(string[] args)
{
CopyReadAndDelete(#"c:\Documents\~$test.xlsx");
}
static void CopyReadAndDelete(string filePath)
{
var tempFileFullPath = Path.Combine(Path.GetDirectoryName(filePath), Guid.NewGuid().ToString());
File.Copy(filePath, tempFileFullPath);
try
{
using (var sr = new StreamReader(tempFileFullPath))
{
Console.WriteLine(sr.ReadToEnd()); //or do anything with the content
}
}
finally
{
File.Delete(tempFileFullPath);
}
}

C# Drag and Drop attached file from outlook email

I want to use/read attached files from an outlook email into a WinForm solution.
Ex: the email has a TXT file attached; I want to perform a Drag&Drog of the TXT file into the WinForm and read the TXT at the same time.
This is an old question, but I'll provide another answer anyhow that doesn't involve using the Outlook objects.
This URL provides working code that is about 13 years old, but still seems to work, on how to handle the "FileGroupDescriptor" and "FileContents" data that Outlook passes to the DropDrop event. Just in case that link dies, here is the relevant code, copy/pasted directly:
DragEnter event:
private void Form1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
{
// for this program, we allow a file to be dropped from Explorer
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{ e.Effect = DragDropEffects.Copy;}
// or this tells us if it is an Outlook attachment drop
else if (e.Data.GetDataPresent("FileGroupDescriptor"))
{ e.Effect = DragDropEffects.Copy;}
// or none of the above
else
{ e.Effect = DragDropEffects.None;}
}
DragDrop event:
private void Form1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
string [] fileNames = null;
try
{
if ( e.Data.GetDataPresent(DataFormats.FileDrop,false) == true)
{
fileNames = (string []) e.Data.GetData(DataFormats.FileDrop);
// handle each file passed as needed
foreach( string fileName in fileNames)
{
// do what you are going to do with each filename
}
}
else if (e.Data.GetDataPresent("FileGroupDescriptor"))
{
//
// the first step here is to get the filename
// of the attachment and
// build a full-path name so we can store it
// in the temporary folder
//
// set up to obtain the FileGroupDescriptor
// and extract the file name
Stream theStream = (Stream) e.Data.GetData("FileGroupDescriptor");
byte [] fileGroupDescriptor = new byte[512];
theStream.Read(fileGroupDescriptor,0,512);
// used to build the filename from the FileGroupDescriptor block
StringBuilder fileName = new StringBuilder("");
// this trick gets the filename of the passed attached file
for(int i=76; fileGroupDescriptor[i]!=0; i++)
{ fileName.Append(Convert.ToChar(fileGroupDescriptor[i]));}
theStream.Close();
string path = Path.GetTempPath();
// put the zip file into the temp directory
string theFile = path+fileName.ToString();
// create the full-path name
//
// Second step: we have the file name.
// Now we need to get the actual raw
// data for the attached file and copy it to disk so we work on it.
//
// get the actual raw file into memory
MemoryStream ms = (MemoryStream) e.Data.GetData(
"FileContents",true);
// allocate enough bytes to hold the raw data
byte [] fileBytes = new byte[ms.Length];
// set starting position at first byte and read in the raw data
ms.Position = 0;
ms.Read(fileBytes,0,(int)ms.Length);
// create a file and save the raw zip file to it
FileStream fs = new FileStream(theFile,FileMode.Create);
fs.Write(fileBytes,0,(int)fileBytes.Length);
fs.Close(); // close the file
FileInfo tempFile = new FileInfo(theFile);
// always good to make sure we actually created the file
if ( tempFile.Exists == true)
{
// for now, just delete what we created
tempFile.Delete();
}
else
{ Trace.WriteLine("File was not created!");}
}
}
catch (Exception ex)
{
Trace.WriteLine("Error in DragDrop function: " + ex.Message);
// don't use MessageBox here - Outlook or Explorer is waiting !
}
}
Note that this code doesn't Dispose of objects that it should, such as the MemoryStream and FileStream objects.
You can get the running Outlook instance by using the GetActiveObject method which allows to obtain a running instance of the specified object from the running object table (ROT). Then you can automate Outlook to get the currently selected or opened item from which an attachment might be dragged. See C# app automates Outlook (CSAutomateOutlook) for the sample code.

SharpZipLib : Compressing a single file to a single compressed file

I am currently working with SharpZipLib under .NET 2.0 and via this I need to compress a single file to a single compressed archive. In order to do this I am currently using the following:
string tempFilePath = #"C:\Users\Username\AppData\Local\Temp\tmp9AE0.tmp.xml";
string archiveFilePath = #"C:\Archive\Archive_[UTC TIMESTAMP].zip";
FileInfo inFileInfo = new FileInfo(tempFilePath);
ICSharpCode.SharpZipLib.Zip.FastZip fZip = new ICSharpCode.SharpZipLib.Zip.FastZip();
fZip.CreateZip(archiveFilePath, inFileInfo.Directory.FullName, false, inFileInfo.Name);
This works exactly (ish) as it should, however while testing I have encountered a minor gotcha. Lets say that my temp directory (i.e. the directory that contains the uncompressed input file) contains the following files:
tmp9AE0.tmp.xml //The input file I want to compress
xxx_tmp9AE0.tmp.xml // Some other file
yyy_tmp9AE0.tmp.xml // Some other file
wibble.dat // Some other file
When I run the compression all the .xml files are included in the compressed archive. The reason for this is because of the final fileFilter parameter passed to the CreateZip method. Under the hood SharpZipLib is performing a pattern match and this also picks up the files prefixed with xxx_ and yyy_. I assume it would also pick up anything postfixed as well.
So the question is, how can I compress a single file with SharpZipLib? Then again maybe the question is how can I format that fileFilter so that the match can only ever pick up the file I want to compress and nothing else.
As an aside, is there any reason as to why System.IO.Compression not include a ZipStream class? (It only supports GZipStream)
EDIT : Solution (Derived from accepted answer from Hans Passant)
This is the compression method I implemented:
private static void CompressFile(string inputPath, string outputPath)
{
FileInfo outFileInfo = new FileInfo(outputPath);
FileInfo inFileInfo = new FileInfo(inputPath);
// Create the output directory if it does not exist
if (!Directory.Exists(outFileInfo.Directory.FullName))
{
Directory.CreateDirectory(outFileInfo.Directory.FullName);
}
// Compress
using (FileStream fsOut = File.Create(outputPath))
{
using (ICSharpCode.SharpZipLib.Zip.ZipOutputStream zipStream = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(fsOut))
{
zipStream.SetLevel(3);
ICSharpCode.SharpZipLib.Zip.ZipEntry newEntry = new ICSharpCode.SharpZipLib.Zip.ZipEntry(inFileInfo.Name);
newEntry.DateTime = DateTime.UtcNow;
zipStream.PutNextEntry(newEntry);
byte[] buffer = new byte[4096];
using (FileStream streamReader = File.OpenRead(inputPath))
{
ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(streamReader, zipStream, buffer);
}
zipStream.CloseEntry();
zipStream.IsStreamOwner = true;
zipStream.Close();
}
}
}
This is an XY problem, just don't use FastZip. Follow the first example on this web page to avoid accidents.

Why can't this file be deleted after using C1ZipFile?

The following code gives me a System.IO.IOException with the message 'The process cannot access the file'.
private void UnPackLegacyStats()
{
DirectoryInfo oDirectory;
XmlDocument oStatsXml;
//Get the directory
oDirectory = new DirectoryInfo(msLegacyStatZipsPath);
//Check if the directory exists
if (oDirectory.Exists)
{
//Loop files
foreach (FileInfo oFile in oDirectory.GetFiles())
{
//Check if file is a zip file
if (C1ZipFile.IsZipFile(oFile.FullName))
{
//Open the zip file
using (C1ZipFile oZipFile = new C1ZipFile(oFile.FullName, false))
{
//Check if the zip contains the stats
if (oZipFile.Entries.Contains("Stats.xml"))
{
//Get the stats as a stream
using (Stream oStatsStream = oZipFile.Entries["Stats.xml"].OpenReader())
{
//Load the stats as xml
oStatsXml = new XmlDocument();
oStatsXml.Load(oStatsStream);
//Close the stream
oStatsStream.Close();
}
//Loop hit elements
foreach (XmlElement oHitElement in oStatsXml.SelectNodes("/*/hits"))
{
//Do stuff
}
}
//Close the file
oZipFile.Close();
}
}
//Delete the file
oFile.Delete();
}
}
}
I am struggling to see where the file could still be locked. All objects that could be holding onto a handle to the file are in using blocks and are explicitly closed.
Is it something to do with using FileInfo objects rather than the strings returned by the static GetFiles method?
Any ideas?
I do not see problems in your code, everything look ok. To check is the problem lies in C1ZipFile I suggest you initialize zip from stream, instead of initialization from file, so you close stream explicitly:
//Open the zip file
using (Stream ZipStream = oFile.OpenRead())
using (C1ZipFile oZipFile = new C1ZipFile(ZipStream, false))
{
// ...
Several other suggestions:
You do not need to call Close() method, with using (...), remove them.
Move xml processing (Loop hit elements) outsize zip processing, i.e. after zip file closeing, so you keep file opened as least as possible.
I assume you're getting the error on the oFile.Delete call. I was able to reproduce this error. Interestingly, the error only occurs when the file is not a zip file. Is this the behavior you are seeing?
It appears that the C1ZipFile.IsZipFile call is not releasing the file when it's not a zip file. I was able to avoid this problem by using a FileStream instead of passing the file path as a string (the IsZipFile function accepts either).
So the following modification to your code seems to work:
if (oDirectory.Exists)
{
//Loop files
foreach (FileInfo oFile in oDirectory.GetFiles())
{
using (FileStream oStream = new FileStream(oFile.FullName, FileMode.Open))
{
//Check if file is a zip file
if (C1ZipFile.IsZipFile(oStream))
{
// ...
}
}
//Delete the file
oFile.Delete();
}
}
In response to the original question in the subject: I don't know if it's possible to know if a file can be deleted without attempting to delete it. You could always write a function that attempts to delete the file and catches the error if it can't and then returns a boolean indicating whether the delete was successful.
I'm just guessing: are you sure that oZipFile.Close() is enough? Perhaps you have to call oZipFile.Dispose() or oZipFile.Finalize() to be sure it has actually released the resources.
More then Likely it's not being disposed, anytime you access something outside of managed code(streams, files, etc.) you MUST dispose of them. I learned the hard way with Asp.NET and Image files, it will fill up your memory, crash your server, etc.
In the interest of completeness I am posing my working code as the changes came from more than one source.
private void UnPackLegacyStats()
{
DirectoryInfo oDirectory;
XmlDocument oStatsXml;
//Get the directory
oDirectory = new DirectoryInfo(msLegacyStatZipsPath);
//Check if the directory exists
if (oDirectory.Exists)
{
//Loop files
foreach (FileInfo oFile in oDirectory.GetFiles())
{
//Set empty xml
oStatsXml = null;
//Load file into a stream
using (Stream oFileStream = oFile.OpenRead())
{
//Check if file is a zip file
if (C1ZipFile.IsZipFile(oFileStream))
{
//Open the zip file
using (C1ZipFile oZipFile = new C1ZipFile(oFileStream, false))
{
//Check if the zip contains the stats
if (oZipFile.Entries.Contains("Stats.xml"))
{
//Get the stats as a stream
using (Stream oStatsStream = oZipFile.Entries["Stats.xml"].OpenReader())
{
//Load the stats as xml
oStatsXml = new XmlDocument();
oStatsXml.Load(oStatsStream);
}
}
}
}
}
//Check if we have stats
if (oStatsXml != null)
{
//Process XML here
}
//Delete the file
oFile.Delete();
}
}
}
The main lesson I learned from this is to manage file access in one place in the calling code rather than letting other components manage their own file access. This is most apropriate when you want to use the file again after the other component has finished it's task.
Although this takes a little more code you can clearly see where the stream is disposed (at the end of the using), compared to having to trust that a component has correctly disposed of the stream.

Can an App.Config be loaded from a string or memory stream?

I know that I can load an app.config file from a different location using the following line of code:
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", ConfigFile);
where ConfigFile is a full path location. What I'd like to do though is be able to load a file that has been encrypted for the app.config. Ideally, I'd like to be able to load the file, decrypt it, and load it into a string or memory stream and pass it to the app as if it were the app.config. I know I could just load all of the values from it and access them manually, but I'd like to be able to access them using the built in functionality of .NET. Is there a way to tell the app to use the config file from something other than a file?
The other option is to open the file, decrypt it, write it out to a temp file, and then use the above code to reference it that way, but if there was an easier way, ideally, I'd like to find it, to have to avoid dealing with additional files.
While I was never able to get an answer for this so far, I had to come up with a work-around. This may not be the best solution, but it does work. Basically what we've done is encypted our app.config file, and given it a new name. When the app starts up, it will take the encypted file, decyrpt it, and write it to a Windows temp file. This ensures that the file is some unique random name that no one is likely to find, and we don't have to manage the files, as Windows will delete it for us automatically. This way each re-launch we are able to re-write out a new file and use it. Here's the basic code snippets for anyone who is interested.
This first method, LoadFileAppConfig(), will load up the file. In this case, since they are services, we need to load the executing path, and pass it to the appropriate method. We get back the path of the decrypted app.config, and then use the SetData() method to set it to be the app.config path.
/// <summary>
/// Loads the Local App.Config file, and sets it to be the local app.config file
/// </summary>
/// <param name="p_ConfigFilePath">The path of the config file to load, i.e. \Logs\</param>
public void LoadFileAppConfig(string p_ConfigFilePath)
{
try
{
// The app.config path is the passed in path + Application Name + .config
m_LocalAppConfigFile = ProcessLocalAppConfig(p_ConfigFilePath + this.ApplicationName + ".config");
// This sets the service's app.config property
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", m_LocalAppConfigFile);
}
catch (Exception ex)
{
throw ex;
}
}
In this method we are getting the path of the file, passing that file off to be decrypted and returned as a string, and then writing that file to our Windows temp file.
public string ProcessLocalAppConfig(string p_ConfigFilePath)
{
try
{
string fileName = Path.GetTempFileName();
string unencryptedConfig = DecryptConfigData(p_ConfigFilePath);
FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write);
StreamWriter streamWriter = new StreamWriter(fileStream);
if (!string.IsNullOrEmpty(unencryptedConfig))
{
try
{
streamWriter.BaseStream.Seek(0, SeekOrigin.End);
streamWriter.WriteLine(unencryptedConfig);
}
catch (IOException ex)
{
Debug.Assert(false, ex.ToString());
}
finally
{
streamWriter.Close();
}
return fileName;
}
return null;
}
catch (Exception)
{
throw;
}
}
This final method takes in the path of the encrypted app.config, uses our Decryption tool to decrypt the file (ensuring that we can decrypt it, and that it is the right file type) and then returning the decrypted contents as a string to the method above.
private string DecryptConfigData(string p_AppConfigFile)
{
string decryptedData = null;
TMS.Pearl.SystemFramework.CryptographyManager.CryptographyManager cryptManager = new TMS.Pearl.SystemFramework.CryptographyManager.CryptographyManager();
try
{
//Attempt to load the file.
if (File.Exists(p_AppConfigFile))
{
//Load the file's contents and decrypt them if they are encrypted.
string rawData = File.ReadAllText(p_AppConfigFile);
if (!string.IsNullOrEmpty(rawData))
{
if (!rawData.Contains("<?xml")) //assuming that all unencrypted config files will start with an xml tag...
{
decryptedData = cryptManager.Decrypt(rawData);
}
else
{
decryptedData = rawData;
}
}
}
}
catch (Exception)
{
throw;
}
return decryptedData;
}

Categories

Resources