How can I change the directory for the encrypted files? - c#

I have developed this C# System that authentically encrypts files using AES-GCM algorithm. However, I don't want the encrypted file to be in same folder as original folder. How can I change it? Below is the sniplet of my code and where exactly I need help.
foreach (string inputFilePath in inputFilePaths)
{
// Proceed if file exists
if (File.Exists(inputFilePath))
{
try
{
// Encrypt
byte[] key = PasswordAsKey();
string[] encryptedFileContents = AesGcmFileEncryption.Encrypt(inputFilePath, key);
// Here is where I need clarification
string outputFilePath = inputFilePath;
outputFilePath += ".AEncrypt";
if (File.Exists(outputFilePath))
{
skippedBecauseFileExists = true;
}
else
{
File.WriteAllLines(outputFilePath, encryptedFileContents);
counter++;
// Status
label10.Text = "Copied and encrypted \"" + Path.GetFileName(inputFilePath) + "\"";
}
}
catch (Exception ex)

You can create subdirectory and write the encrypted files there.
string inputDir = Path.GetDirectoryName(inputFilePath);
string outputDir = Path.Combine(inputDir, "EncryptedFiles");
Directory.CreateDirectory(outputDir);
string outputFileName = Path.GetFileName(inputFilePath) + ".AEncrypt";
string outputFilePath = Path.Combine(outputDir, outputFileName);
Here I extract directory name from inputFilePath and append "EncryptedFiles" to it. Then I create your new file name and append it to the generated directory.
Directory.CreateDirectory() will create directory if it doesn't exist.

Related

TextWriter not working because its being used by another process. Network 5.0

I am trying to create a console app on network version 5.0 using visual studio. The purpose is to read all the PNG files in a directory, and make a JSON file with the code:
{
"format_version": "1.16.100",
"minecraft:texture_set": {
"color": "*Filename*",
"metalness_emissive_roughness": "*Filename_mer*"
}
}
In it. And yes this is for Minecraft. The filename and the filename_mer are automatically filled in by code, but that isn't my issue.
static void Main(string[] args)
{
Console.WriteLine("Goto " + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Packages\\Microsoft.MinecraftUWP_8wekyb3d8bbwe\\LocalState\\games\\com.mojang\\resource_packs" + " And find the resource pack you want to generate files into");
string pathname = Console.ReadLine();
#region Message
Console.WriteLine();
Console.WriteLine("Looking for folder...");
#endregion
string path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Packages\\Microsoft.MinecraftUWP_8wekyb3d8bbwe\\LocalState\\games\\com.mojang\\resource_packs\\" + pathname + "\\textures";
#region Message
Console.WriteLine();
Console.WriteLine("Folder found in");
Console.WriteLine(path);
#endregion
string[] directories = Directory.GetDirectories(path);
foreach (string paths in directories)
{
string[] files = Directory.GetFiles(paths);
foreach (string file in files)
{
string filenameWithType = file.Substring(file.LastIndexOf("\\") + 1);
string filename = filenameWithType.Substring(0, filenameWithType.LastIndexOf("."));
string thisPath = file.Substring(0, file.LastIndexOf("\\"));
if (filenameWithType.Substring(filenameWithType.LastIndexOf(".") + 1) == "png")
{
string newPath = thisPath + "\\" + filename + ".texture_set.json";
File.Create(newPath);
List<string> codeInFile = new List<string>();
codeInFile.Clear();
codeInFile.Add("{");
codeInFile.Add("\"format_version\": \"1.16.100\",");
codeInFile.Add("\"minecraft:texture_set\": {");
codeInFile.Add("\"color\": \"" + filename + "\",");
codeInFile.Add("\"metalness_emissive_roughness\": \"" + filename + "_mer\"");
codeInFile.Add("}");
codeInFile.Add("}");
TextWriter tw = new StreamWriter(newPath, false);
foreach (string line in codeInFile)
{
tw.WriteLine(line);
Console.WriteLine(line);
}
tw.Close();
string newPathtxt = thisPath + "\\" + filename + "_mer.png";
Bitmap bitmap = new Bitmap(file);
using (Bitmap b = new Bitmap(bitmap.Width, bitmap.Height))
{
using (Graphics g = Graphics.FromImage(b))
{
g.Clear(Color.Green);
}
b.Save(newPathtxt, ImageFormat.Png);
}
}
}
}
#region Message
Console.WriteLine("");
Console.WriteLine("All done, Your good to go!");
#endregion
Console.Read();
}
This is all my code, on the file. It reads a directory which you enter, looks through the textures file, and for each PNG file in there it creates a JSON file with the code specified earlier. Although when run the code gives me this error.
System.IO.IOException: 'The process cannot access the file 'C:\Users\https\AppData\Local\Packages\Microsoft.MinecraftUWP_8wekyb3d8bbwe\LocalState\games\com.mojang\resource_packs\Vanilla_Resource_Pack_1.17.10\textures\blocks\acacia_trapdoor.texture_set.json' because it is being used by another process.'
I cannot find any answers which fit my issue, I would appreciate any help.
This is a Minecraft Bedrock edition resource pack btw, not sure if that helps.
Don't do this File.Create(newPath); or rethink your problem. It looks like a typo.
In short File.Create(newPath) is creating a FileStream and discarding it, leaving the file open and with a share lock. If you are trying to pre-create the file, at least use the using statement:
using (File.Create(newPath));
or Close
File.Create(newPath).Close();
or
File.WriteAllText(newPath, String.Empty);
or
File.WriteAllBytes(newPath, Array.Empty<byte>());
If you are just trying to create or overwrite the file StreamWriter(newPath, false) is good enough.

DotNetZip wrong password while unzipping

Scenario: upload file than try zip it using DotNetZip with password protection, password is generated with Membership.GeneratePassword() method. Everything is working fine except that sometimes user is not able to unzip files with generated password. Wired thing is that this happens only sometimes let's say 1 out of 15 times.
Generate password:
public static String FilePassword()
{
while (_filePassword.Length < 12)
{
_filePassword += string.Concat(Membership.GeneratePassword(1, 0).Where(char.IsLetterOrDigit));
}
return _filePassword;
}
Save file:
if (FileUploadControl.HasFile)
{
fileName = Path.GetFileName(FileUploadControl.FileName);
FileUploadControl.SaveAs(FileSavePath + fileName);
// Archive uploaded file to zip.
using (ZipFile zip = new ZipFile())
{
// File to be archived.
var file = FileUploadControl.PostedFile;
// Enable encryption of file
zip.Encryption = EncryptionAlgorithm.PkzipWeak;
// Set password.
zip.Password = Settings.FilePassword();
// Set temporary folder path for archive process.
zip.TempFileFolder = tempPath;
// Add file to archive with its path.
zip.AddFile(FileSavePath + file.FileName, "");
File objFile = new File(file.FileName, FileSavePath);
// Save zip file with the name as file ID.
zip.Save(FileSavePath + file.FileName);
}
}
I logged password while creating in method and also while protecting zip file with password, they are always matching, I cannot see what's wrong, why sometimes while unzipping file it shows wrong password.
Why do you use the static global variable _filePassword in FilePassword() instead of one in scope?
This way it could be modified from outside, or possible even still contain the last used value. Also it isn't thread safe without lock.
Settle with a local variable and it should be fine.
public static String FilePassword()
{
string retString = string.Empty;
while (retString.Length < 12)
{
retString += string.Concat(Membership.GeneratePassword(1, 0).Where(char.IsLetterOrDigit));
}
return retString;
}
You can log a return Value too.
Sample for understanding
if (FileUploadControl.HasFile)
{
fileName = Path.GetFileName(FileUploadControl.FileName);
FileUploadControl.SaveAs(FileSavePath + fileName);
string filePassword = Settings.FilePassword(); // Contains the Password
using (ZipFile zip = new ZipFile())
{
var file = FileUploadControl.PostedFile;
zip.Encryption = EncryptionAlgorithm.PkzipWeak;
zip.Password = filePassword; // <-- Set password for ZIP
zip.TempFileFolder = tempPath;
zip.AddFile(FileSavePath + file.FileName, "");
File objFile = new File(file.FileName, FileSavePath);
zip.Save(FileSavePath + file.FileName);
}
// Log this value!
Log(filePassword);
}

Creating unique files in another directory

Currently i am taking in multiple .txt files from a directory i have specified (sourceDirectory). I am generating new .csv files with the same name as the .txt files - one .csv file for each .txt file.
However i want to generate these new files in another directory which i have specified (directoryPath). If i run my program once it creates these files in the initial directory, however if i run my program again it now generates the files in the destination directory.
The following is my code where i complete the above:
static void Main(string[] args)
{
string sourceDirectory = #"C:directoryWhereTXTFilesAre";
var txtFiles = Directory.EnumerateFiles(sourceDirectory, "*.txt", SearchOption.AllDirectories);
foreach (string currentFile in txtFiles)
{
readFile(currentFile);
}
string directoryPath = #"C:\destinationForCSVFiles";
}
I then create the new .csv files name based on the original .txt file like this:
static FileStream CreateFileWithUniqueName(string folder, string fileName, int maxAttempts = 1024)
{
var fileBase = Path.GetFileNameWithoutExtension(fileName);
var ext = Path.GetExtension(fileName);
// build hash set of filenames for performance
var files = new HashSet<string> (Directory.GetFiles(folder));
for (var index = 0; index < maxAttempts; index++)
{
// first try with the original filename, else try incrementally adding an index
var name = (index == 0)
? fileName
: String.Format("{0} ({1}){2}", fileBase, index, ext);
// check if exists
var fullPath = Path.Combine(folder, name);
string CSVfileName = Path.ChangeExtension(fullPath, ".csv");
if (files.Contains(CSVfileName))
continue;
// try to create the file
try
{
return new FileStream(CSVfileName, FileMode.CreateNew, FileAccess.Write);
}
catch (DirectoryNotFoundException) { throw; }
catch (DriveNotFoundException) { throw; }
catch (IOException)
{
}
}
I don't see why it's creating the .csv files initially in the same directory that the .txt files are in and then second time i run my code it creates them in the directoryPath.
Desired output: sourceDirectory left as it is with only .txt files and directoryPath to hold the .csv files.
The only other place i call CreateFileWithUniqueName is within my readFile method, the code is below:
using (var stream = CreateFileWithUniqueName(#"C:destinationFilePath", currentFile))
{
Console.WriteLine("Created \"" + stream.Name + "\"");
newFileName = stream.Name;
Globals.CleanedFileName = newFileName;
}
It seems that you are passing the full filename of the source file. This confuses the Path.Combine inside the CreateFileWithUniqueFilename because you are falling in this subtle remarks found on the documentation of Path.Combine
paths should be an array of the parts of the path to combine. If the
one of the subsequent paths is an absolute path, then the combine
operation resets starting with that absolute path, discarding all
previous combined paths.
You can fix it easily with
using (var stream = CreateFileWithUniqueName(#"C:\destinationFilePath",
Path.GetFileName(currentFile)))
{
Console.WriteLine("Created \"" + stream.Name + "\"");
newFileName = stream.Name;
Globals.CleanedFileName = newFileName;
}
Or better extract the filename without path inside the CreateFileWithUniqueName
static FileStream CreateFileWithUniqueName(string folder, string fileName, int maxAttempts = 1024)
{
var fileBase = Path.GetFileName(fileName);
fileBase = Path.GetFileNameWithoutExtension(fileBase);
var ext = Path.GetExtension(fileBase);
also, you should build your CSVfileName using the cleaned filename
var name = (index == 0)
? String.Format("{0}{1}", fileBase, ext);
: String.Format("{0} ({1}){2}", fileBase, index, ext);
var fullPath = Path.Combine(folder, name);
string CSVfileName = Path.ChangeExtension(fullPath, ".csv");
if (files.Contains(CSVfileName))
continue;

Download files from SFTP with SSH.NET library

string host = #"ftphost";
string username = "user";
string password = "********";
string localFileName = System.IO.Path.GetFileName(#"localfilename");
string remoteDirectory = "/export/";
using (var sftp = new SftpClient(host, username, password))
{
sftp.Connect();
var files = sftp.ListDirectory(remoteDirectory);
foreach (var file in files)
{
if (!file.Name.StartsWith("."))
{
string remoteFileName = file.Name;
if (file.LastWriteTime.Date == DateTime.Today)
Console.WriteLine(file.FullName);
File.OpenWrite(localFileName);
string sDir = #"localpath";
Stream file1 = File.OpenRead(remoteDirectory + file.Name);
sftp.DownloadFile(remoteDirectory, file1);
}
}
}
I am using SSH.NET (Renci.SshNet) library to work with an SFTP server. What I need to do is grab files from a specific folder on the SFTP server based on today's date. Then copy those files from the SFTP server to a local drive a server of mine.
Above is the code I have but it is not working. Sometimes it says file does not exist but sometimes the files I will be downloading will not be on my local servers but I need to download whatever files were uploaded to the remote folder for that day.
Can someone take a look and see what is wrong? I believe it has something to do with the stream portion. I have worked with FTP much besides uploading files which I took some previous code I had and thought I could reverse the process but that isn't working. The code I used is based off of this example.
A simple working code to download a file with SSH.NET library is:
using (Stream fileStream = File.Create(#"C:\target\local\path\file.zip"))
{
sftp.DownloadFile("/source/remote/path/file.zip", fileStream);
}
See also Downloading a directory using SSH.NET SFTP in C#.
To explain, why your code does not work:
The second parameter of SftpClient.DownloadFile is a stream to write a downloaded contents to.
You are passing in a read stream instead of a write stream. And moreover the path you are opening read stream with is a remote path, what cannot work with File class operating on local files only.
Just discard the File.OpenRead line and use a result of previous File.OpenWrite call instead (that you are not using at all now):
Stream file1 = File.OpenWrite(localFileName);
sftp.DownloadFile(file.FullName, file1);
Or even better, use File.Create to discard any previous contents that the local file may have.
I'm not sure if your localFileName is supposed to hold full path, or just file name. So you may need to add a path too, if necessary (combine localFileName with sDir?)
While the example works, its not the correct way to handle the streams...
You need to ensure the closing of the files/streams with the using clause..
Also, add try/catch to handle IO errors...
public void DownloadAll()
{
string host = #"sftp.domain.com";
string username = "myusername";
string password = "mypassword";
string remoteDirectory = "/RemotePath/";
string localDirectory = #"C:\LocalDriveFolder\Downloaded\";
using (var sftp = new SftpClient(host, username, password))
{
sftp.Connect();
var files = sftp.ListDirectory(remoteDirectory);
foreach (var file in files)
{
string remoteFileName = file.Name;
if ((!file.Name.StartsWith(".")) && ((file.LastWriteTime.Date == DateTime.Today))
using (Stream file1 = File.Create(localDirectory + remoteFileName))
{
sftp.DownloadFile(remoteDirectory + remoteFileName, file1);
}
}
}
}
My version of #Merak Marey's Code. I am checking if files exist already and different download directories for .txt and other files
static void DownloadAll()
{
string host = "xxx.xxx.xxx.xxx";
string username = "###";
string password = "123";string remoteDirectory = "/IN/";
string finalDir = "";
string localDirectory = #"C:\filesDN\";
string localDirectoryZip = #"C:\filesDN\ZIP\";
using (var sftp = new SftpClient(host, username, password))
{
Console.WriteLine("Connecting to " + host + " as " + username);
sftp.Connect();
Console.WriteLine("Connected!");
var files = sftp.ListDirectory(remoteDirectory);
foreach (var file in files)
{
string remoteFileName = file.Name;
if ((!file.Name.StartsWith(".")) && ((file.LastWriteTime.Date == DateTime.Today)))
{
if (!file.Name.Contains(".TXT"))
{
finalDir = localDirectoryZip;
}
else
{
finalDir = localDirectory;
}
if (File.Exists(finalDir + file.Name))
{
Console.WriteLine("File " + file.Name + " Exists");
}else{
Console.WriteLine("Downloading file: " + file.Name);
using (Stream file1 = File.OpenWrite(finalDir + remoteFileName))
{
sftp.DownloadFile(remoteDirectory + remoteFileName, file1);
}
}
}
}
Console.ReadLine();
}
Massimiliano's solution has one problem which will lead to files not being completely downloaded. The FileStream must be closed. This is especially a problem for encrypted files. They won't completely decrypt intermittently without closing the stream.
var files = sftp.ListDirectory(remoteVendorDirectory).Where(f => !f.IsDirectory);
foreach (var file in files)
{
var filename = $"{LocalDirectory}/{file.Name}";
if (!File.Exists(filename))
{
Console.WriteLine("Downloading " + file.FullName);
using (var localFile = File.OpenWrite(filename))
sftp.DownloadFile(file.FullName, localFile);
}
}
This solves the problem on my end.
var files = sftp.ListDirectory(remoteVendorDirectory).Where(f => !f.IsDirectory);
foreach (var file in files)
{
var filename = $"{LocalDirectory}/{file.Name}";
if (!File.Exists(filename))
{
Console.WriteLine("Downloading " + file.FullName);
using (var localFile = File.OpenWrite(filename))
sftp.DownloadFile(file.FullName, localFile);
}
}
Without you providing any specific error message, it's hard to give specific suggestions.
However, I was using the same example and was getting a permissions exception on File.OpenWrite - using the localFileName variable, because using Path.GetFile was pointing to a location that obviously would not have permissions for opening a file > C:\ProgramFiles\IIS(Express)\filename.doc
I found that using System.IO.Path.GetFileName is not correct, use System.IO.Path.GetFullPath instead, point to your file starting with "C:\..."
Also open your solution in FileExplorer and grant permissions to asp.net for the file or any folders holding the file. I was able to download my file at that point.

c# service renaming files!

I have a windows service , that takes files with metadata(FIDEF) and corresponding video file and , translates the XML(FIDEF) using XSLT .
I get the file directory listing for FIDEF's and if a video file of the same name exists it translates it. That works ok , but it is on a timer to search every minute. I am trying to handle situations where the same file name enters the input directory but is already in the output directory. I just have it changing the output name to (copy) thus if another file enters i should get (copy)(copy).mov but the service won't start with filenames of the same directory already in the output , it works once and then does not seem to pick up any new files.
Any Help would be great as I have tried a few things with no good results. I believe its the renaming methods, but I've put most of the code up in case its a clean up issue or something else.
(forgive some of the names just trying different things).
private void getFileList()
{
//Get FILE LIST FROM Directory
try
{
// Process Each String/File In Directory
string result;
//string filename;
filepaths = null;
filepaths = Directory.GetFiles(path, Filetype);
foreach (string s in filepaths)
{
for (int i = 0; i < filepaths.Length; i++)
{
//Result Returns Video Name
result = Path.GetFileNameWithoutExtension(filepaths[i]);
FileInfo f = new FileInfo(filepaths[i]);
PreformTranslation(f, outputPath + result , result);
}
}
}
catch (Exception e)
{
EventLog.WriteEntry("Error " + e);
}
}
private void MoveVideoFiles(String Input, String Output)
{
File.Move(Input, Output);
}
private string GetUniqueName(string name)
{
//Original Filename
String ValidName = name;
//remove FIDEF from filename
String Justname1 = Path.GetFileNameWithoutExtension(name);
//get .mov extension
String Extension2 = Path.GetExtension(Justname1);
//get filename with NO extensions
String Justname = Path.GetFileNameWithoutExtension(Justname1);
//get .Fidef
String Extension = Path.GetExtension(name);
int cnt = 0;
//string[] FileName = Justname.Split('(');
//string Name = FileName[0];
while (File.Exists(ValidName)==true)
{
ValidName = outputPath + Justname + "(Copy)" + Extension2 + Extension;
cnt++;
}
return ValidName;
}
private string getMovFile(string name)
{
String ValidName = name;
String Ext = Path.GetExtension(name);
String JustName = Path.GetFileNameWithoutExtension(name);
while(File.Exists(ValidName))
{
ValidName = outputPath + JustName + "(Copy)" + Ext;
}
return ValidName;
}
//Preforms the translation requires XSL & FIDEF name.
private void PreformTranslation(FileInfo FileName, String OutputFileName , String result)
{
string FidefName = OutputFileName + ".FIDEF";
String CopyName;
String copyVidName = outputPath + result;
XslCompiledTransform myXslTransform;
myXslTransform = new XslCompiledTransform();
try
{
myXslTransform.Load(XSLname);
}
catch
{
EventLog.WriteEntry("Error in loading XSL");
}
try
{ //only process FIDEF's with corresponding Video file
if (AllFidef == "no")
{
//Check if video exists if yes,
if (File.Exists(path + result))
{
//Check for FIDEF File Already Existing in the Output Directory.
if (File.Exists(FidefName))
{
//Get unique name
CopyName = GetUniqueName(FidefName);
copyVidName= getMovFile(copyVidName);
//Translate and create new FIDEF.
//double checking the file is here
if (File.Exists(outputPath + result))
{
myXslTransform.Transform(FileName.ToString(), CopyName);
File.Delete(FileName.ToString());
MoveVideoFiles(path + result, copyVidName);
}
////Move Video file with Corresponding Name.
}
else
{ //If no duplicate file exsists in Directory just move.
myXslTransform.Transform(FileName.ToString(), OutputFileName + ".FIDEF");
MoveVideoFiles(path + result, outputPath + result);
}
}
}
else
{
//Must have FIDEF extension
//Processes All FIDEFS and moves any video files if found.
myXslTransform.Transform(FileName.ToString(), OutputFileName + ".FIDEF");
if (File.Exists(path + result))
{
MoveVideoFiles(path + result, outputPath + result);
}
}
}
catch (Exception e)
{
EventLog.WriteEntry("Error Transforming " + "FILENAME = " + FileName.ToString()
+ " OUTPUT_FILENAME = " + OutputFileName + "\r\n" +"\r\n"+ e);
}
}
There is a lot wrong with your code. getFileList has the unneeded inner for loop for starters. Get rid of it. Your foreach loop has s, which can replace filepaths[i] from your for loop. Also, don't do outputPath + result to make file paths. Use Path.Combine(outputPath, result) instead, since Path.Combine handles directory characters for you. Also, you need to come up with a better name for getFileList, since that is not what the method does at all. Do not make your method names liars.
I would simply get rid of MoveVideoFiles. The compiler just might too.
GetUniqueName only works if your file name is of the form name.mov.fidef, which I'm assuming it is. You really need better variable names though, otherwise it will be a maintenance nightware later on. I would get rid of the == true in the while loop condition, but that is optional. The assignment inside the while is why your files get overwritten. You always generate the same name (something(Copy).mov.fidef), and as far as I can see, if the file exists, I think you blow the stack looping forever. You need to fix that loop to generate a new name (and don't forget Path.Combine). Maybe something like this (note this is untested):
int copyCount = 0;
while (File.Exists(ValidName))
{
const string CopyName = "(Copy)";
string copyString = copyCount == 0 ? CopyName : (CopyName + "(" + copyCount + ")");
string tempName = Justname + copyString + Extension2 + Extension;
ValidName = Path.Combine(outputPath, tempName);
copyCount++;
}
This generates something(Copy).mov.fidef for the first copy, something(Copy)(2).mov.fidef for the second, and so on. Maybe not what you want, but you can make adjustments.
At this point you have a lot to do. getMovFile looks as though it could use work in the same manner as GetUniqueName. You'll figure it out. Good luck.

Categories