I'm looking to make a program to make my life easier, I need to be able to easily select a folder, which I can do, I don't need help with that. I want to take the directory of a folder, and put that folder into a new folder with a specified name, and then zip up that folder into a zip format in which I can change the name and filetype of. Is this possible in vanilla C#? I've only ever done files for text and I've never looked at moving and packaging files. SO I'm really clueless, I'd just like to be guided into the right direction.
Edit: I found this code online, but I need to put the folder inside another folder, may I adapt upon this to do so?
string startPath = #"c:\example\start";
string zipPath = #"c:\example\result.zip";
string extractPath = #"c:\example\extract";
ZipFile.CreateFromDirectory(startPath, zipPath);
ZipFile.ExtractToDirectory(zipPath, extractPath);
So, after an extended chat discussion, here's what we've established.
The goal is to put the contents of a source directory into a zip with the following structure:
- Payload
|- name of source
|-- contents of source
Okay, so, starting from an input path called startPath:
var parent = Path.GetDirectoryName(startPath);
var payload = Path.Combine(parent, "payload");
Directory.CreateDirectory(payload); // ensure payload ex
Directory.Move(startPath, Path.Combine(payload, Path.GetFileName(startPath));
var zipPath = Path.Combine(parent, "export.zip");
File.Delete(zipPath);
ZipFile.CreateFromDirectory(payload , zipPath, CompressionLevel.Optimal, true);
The key is that true in the CreateFromDirectory call, that puts the entries in the archive under a directory with the same name as the directory being zipped (in this case, "payload"). Feel free to change CompressionLevel to other values if you want.
Now, this has the side effect of actually physically moving the source directory, which might not be the best user experience. If you want to avoid that, you'll have to basically do what ZipFile.CreateFromDirectory does by hand, which is to enumerate the source directory yourself and then copy the files into the zip archive (in which case you can name those files whatever you want):
var parent = Path.GetDirectoryName(startPath);
var zipPath = Path.Combine(parent, "export.zip");
File.Delete(zipPath);
using var zip = ZipFile.Open(zipPath, ZipArchiveMode.Create);
foreach(var file in Directory.EnumerateFiles(startPath, "*", SearchOption.AllDirectories))
{
// get the path of the file relative to the parent directory
// this gives us a path that starts with the source directory name
// e.g. C:\example\start\file.txt -> start\file.txt
var relativePath = Path.GetRelativePath(parent, file);
// construct the path of the entry in the archive
// this is "Payload", and then the relative path of the file
// we need to fix up the separators because zip entries use /
// e.g. start\file.txt -> Payload/start/file.txt
var entryPath = Path.Combine("Payload", relativePath).Replace(Path.DirectorySeparatorChar, '/');
// put the file in the archive
// to specify a compression level, pass it as the third parameter here
zip.CreateEntryFromFile(file, entryPath);
}
Related
I using .Net Framework 4.0; VS 2015; Ionic.Zip.Reduced (DotNetZip.Reduced) v1.9.1.8. When I try to add a folder to the archive get an exception with the text:
The path is too long
Sample code:
using (var zipFile = new ZipFile(zipFilePath))
{
zipFile.UseZip64WhenSaving = Zip64Option.AsNecessary;
zipFile.AlternateEncodingUsage = ZipOption.Always;
zipFile.AlternateEncoding = Encoding.UTF8;
zipFile.ParallelDeflateThreshold = -1;
var dirPath = #"C:\AAAAAAAAAAA\AAAAAA\AAAAAAAAAAAAAAA\AAAAAAAAA\AAAAAAAAAAAAA\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\";
zipFile.AddDirectory(dirPath); <-Exception
zipFile.Save();
}
In the folder is a file named: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.zip
As a result of an error:
The path is too long
Rewritten in the file-based addition to the archive (using a relative path):
using (var zipFile = new ZipFile(zipFilePath))
{
zipFile.UseZip64WhenSaving = Zip64Option.AsNecessary;
zipFile.AlternateEncodingUsage = ZipOption.Always;
zipFile.AlternateEncoding = Encoding.UTF8;
zipFile.ParallelDeflateThreshold = -1;
var dirPath = #"C:\AAAAAAAAAAA\AAAAAA\AAAAAAAAAAAAAAA\AAAAAAAAA\AAAAAAAAAAAAA\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\";
Directory.SetCurrentDirectory(dirPath);
var files = Directory.GetFiles(dirPath, "*", SearchOption.AllDirectories).ToArray();
foreach (var fullFilePath in files)
{
var fileName = Path.GetFileName(fullFilePath);
var relatedPath = fullFilePath.Substring(0, fullFilePath.LastIndexOf(fileName, StringComparison.InvariantCultureIgnoreCase)).Replace(zipDir, "");
var relatedFilePath = Path.Combine(relatedPath, fileName);
zipFile.AddFile(relatedFilePath); <-Exception
}
zipFile.Save();
}
The error is the same:
The path is too long
I tried to call Path.GetDirectoryName() method, but it also returns an error:
The specified path, file name, or both are too long. The fully
qualified file name must be less than 260 characters, and the
directory name must be less than 248 characters.
I found a lot of solutions but to get to work and did not work (because of the specifics of the application to the new version Framework'a can not go).
Use Framework 4.6.2. Set UseLegacyPathHandling = false option in App.Config or Switch.System.IO.UseLegacyPathHandling = false; Switch.System.IO.BlockLongPaths = false
With the mention of a Group Policy and the inclusion of the option Configuration> Administrative Templates> System> Filesystem> Enable NTFS long paths, or to enable the option via the manifest <ws2:longPathAware>true</ws2:longPathAware>
Use the prefix \\?\ In the path (I understand that for the new version of Framework)
Convert path to the file in 8.3 format using GetShortPathName function .... (Error remains)
Maybe someone faced such problem. I will be glad to any advice. Thanks.
If your path is too long there's not much you can do about it. Even if you can move Windows limits a step further your application won't work well on a non ad-hoc configured system in that scenario.
You can workaround copying the files you have to work with to a temp folder like C:\temp and add the files to the archive from there.
You can even mimic the same folder tree structure with directory names composed of only 1 or 2 letters and then map the complete (but really shorter) directory path to the original path somewhere (on a file for example), so that you can rebuild the original folder tree structure with the same names later on.
http://pastebin.com/DgpMx3Sx
Currently i have this, i need to find a way to make it so that as opposed to writing out the directory of the txt files, i want it to create them in the same location as the exe and access them.
basically i want to change these lines
string location = #"C:\Users\Ryan\Desktop\chaz\log.txt";
string location2 = #"C:\Users\Ryan\Desktop\chaz\loot.txt";
to something that can be moved around your computer without fear of it not working.
If you're saving the files in the same path as the executable file then you can get the directory using:
string appPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
Normally you wouldn't do that, mostly because the install path will be found in the Program Files folders, which require Administrative level access to be modified. You would want to store it in the Application Data folder. That way it is hidden, but commonly accessible through all the users.
You could accomplish such a feat by:
string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string fullPath = Path.Combine(path, #"NameOfApplication");
With those first two lines you'll always have the proper path to a globally accessible location for the application.
Then when you do something you would simply combine the fullPath and the name of the file you attempt to manipulate with FileStream or StreamWriter.
If structured correctly it could be as simple as:
private static void WriteToLog(string file)
{
string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string fullPath = Path.Combine(path, #"NameOfApplication");
// Validation Code should you need it.
var log = Path.Combine(fullPath, file);
using(StreamWriter writer = new StreamWriter(log))
{
// Data
}
}
You could obviously structure or make it better, this is just to provide an example. Hopefully this points you in the right direction, without more specifics then I can't be more help.
But this is how you can access data in a common area and write out to the file of your choice.
I'm working on a project for a class. What I have to do is export parsed instructions to a file. Microsoft has this example which explains how to write to a file:
// Compose a string that consists of three lines.
string lines = "First line.\r\nSecond line.\r\nThird line.";
// Write the string to a file.
System.IO.StreamWriter file = new System.IO.StreamWriter("c:\\test.txt");
file.WriteLine(lines);
file.Close();
I'm fine with that part, but is there a way to write the file to the current project's environment/location? I'd like to do that instead of hard coding a specific path (i.e. "C:\\test.txt").
Yes, just use a relative path. If you use #".\test.txt" ( btw the # just says I'm doing a string literal, it removes the need for the escape character so you could also do ".\\test.txt" and it would write to the same place) it will write the file to the current working directory which in most cases is the folder containing your program.
You can use Assembly.GetExecutingAssembly().Location to get the path of your main assembly (.exe). Do note that if that path is inside a protected folder (for example Program Files) you won't be able to write there unless the user is an administrator - don't rely on this.
Here is sample code:
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
string fileName = Path.Combine(path, "test.txt");
This question / answer shows how to get the user's profile folder where you'll have write access. Alternatively, you can use the user's My Documents folder to save files - again, you're guaranteed to have access to it. You can get that path by calling
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
If you want to get the current folder location of your program use this code :
string path = Directory.GetParent(System.Reflection.Assembly.GetExecutingAssembly().Location).FullName; // return the application.exe current folder
string fileName = Path.Combine(path, "test.txt"); // make the full path as folder/test.text
Full code to write the data to the file :
string path = Directory.GetParent(System.Reflection.Assembly.GetExecutingAssembly().Location).FullName;
string fileName = Path.Combine(path, "test.txt");
if (!File.Exists(fileName))
{
// Create the file.
using (FileStream fs = File.Create(fileName))
{
Byte[] info =
new UTF8Encoding(true).GetBytes("This is some text in the file.");
// Add some information to the file.
fs.Write(info, 0, info.Length);
}
}
How can I bundle a folder with a one click application and reference those files/folders after?
Seems rather simple but I just can't figure out how.
As in, I had the file index.html in the folder UI and I wanted to package that with the application, then I want to get the stream for that file with the string "/UI/index.html" but instead of just index.html, an entire website.
Add the folder to your VS Project, right-click on it and select "embed as resource". That will make the files in the folder be embedded in the .NET assembly. To get the file contents in your program, you can use something like this:
public class ReadResource
{
public string ReadInEmbeddedFile (string filename) {
// assuming this class is in the same assembly as the resource folder
var assembly = typeof(ReadResource).Assembly;
// get the list of all embedded files as string array
string[] res = assembly.GetManifestResourceNames ();
var file = res.Where (r => r.EndsWith(filename)).FirstOrDefault ();
var stream = assembly.GetManifestResourceStream (file);
string file_content = new StreamReader(stream).ReadToEnd ();
return file_content;
}
}
In the above function I assume your files a text/html files; if not, you can change it not to return string but byte[], and use a binary stream reader for that.
I also select the files by file.EndsWith() which is enough for my needs; if your folder has a deep nested structure you need to modify that code to parse for folder levels.
Perhaps there is a better way, but given the content is not too large you can embed binaries directly into your program as a base64 string. In this case it would need to be an archive of the folder. You would also need to embed the dll used for unzipping that archive (If I understood correctly you want to have single .exe and nothing more).
Here is a short example
// create base64 strings prior to deployment
string unzipDll = Convert.ToBase64String(File.ReadAllBytes("Ionic.Zip.dll"));
string archive = Convert.ToBase64String(File.ReadAllBytes("archive.zip"));
string unzipDll = "base64string";
string archive = "probablyaverylongbase64string";
File.WriteAllBytes("Ionic.zip.dll", Convert.FromBase64String(unzipDll));
File.WriteAllBytes("archive.zip", Convert.FromBase64String(archive);
Ionic.Zip.ZipFile archive = new Ionic.Zip.ZipFile(archiveFile);
archive.ExtractAll("/destination");
The unzipping library is DotNetZip. It's nice because you need just a single dll. http://dotnetzip.codeplex.com/downloads/get/258012
Edit:
Come to think of it, as long as you write the Ionic.dll to the working directory of the .exe you shouldn't need to use the dynamic dll loading so I removed that part to simplify the answer (it would still need to be written before you reach the method it is in though).
I wrote a simple console tool that reads a file and then writes something out. I intend to just drag and drop files and then out pops the output in the same directory as the input file.
All of the testing works, and when I call it from command-line, everything comes out as expected. However, when I tried dragging and dropping it in explorer, no files were created.
I did a search through the system and found that they were all dumped at Documents and Settings under my user folder, and when I printed out the full path that's what it said.
Which is weird. Wouldn't Path.GetFullPath return the absolute path of the input file? Instead it looks like it just combined that user directory path to the input's filename.
EDIT: here's the code. I feel like I've made a logic error somewhere but can't seem to see it.
filename = System.IO.Path.GetFileName(args[i]);
abspath = Path.GetFullPath(filename);
dirpath = Path.GetDirectoryName(abspath);
....
Console.WriteLine(dirpath);
Path.GetFullPath should return the absolute path of the path string you pass in.
Path.GetFileName(string path) only returns the filename and extension of the file you pass in. For example, System.IO.Path.GetFileName("C:\SomeDirectory\Test.txt"); would just return "Test.txt". You'll want to use the Path.GetDirectoryName to get the path of your input file, like so:
string inputDirectory = System.IO.Path.GetDirectoryName(args[i]);
Alternately, you can use the FileInfo class to retrieve a bunch more information about your input file. For example:
// Assuming args[i] = "C:\SomeDirectory\Test.txt"
FileInfo inputFile = new FileInfo(args[i]);
string inputDirectory = inputFile.DirectoryName; // "C:\SomeDirectory"
string inputFileName = inputFile.Name; // "Test.txt"
string fullInputFile = inputFile.FullName; // "C:\SomeDirectory\Test.txt"