I want to prompt a user to save a file when some modification has been done to it but the problem is i can't for the life of me do that.
Some people have suggested using FileInfo class but it only gives you the lastWriteTime, LastAccessTime and CreationTime.
I would like to use FileInfo class rather than FileSystemWatcher to check for modifications but how?
Example: Say a user has edited a file, within my program, that they loaded and clicks EXIT, i want a way to check whether any modifications were done on the file. If none, exit. If some, prompt user to save the file. So how do i check for modifications on that FILE?
The easiest way is to calculate the MD5 hash of the file and compare to the original MD5 hash and if these two don't match the file was modified...
using (var md5 = new MD5CryptoServiceProvider())
{
var buffer = md5.ComputeHash(File.ReadAllBytes(filename));
var sb = new StringBuilder();
for (var i = 0; i < buffer.Length; i++)
{
sb.Append(buffer[i].ToString("x2"));
}
return sb.ToString();
}
Here are some examples of how to use the File or FileInfo class to get the LastWriteTime.
http://www.csharp-examples.net/file-creation-modification-time/
I would store the timestamp of the file when you load it, then compare it to the File.GetLastWriteTime() to see if the file has been saved since then. If the file was modified by an outside source, you can give the user the option to discard their changes and reload the file, or save their changes to a new file.
Related
I would like to know if it is possible to check the size of the zip file that is being created dynamically, because I am reading a directory and generate a 19 MB zip and I would like two zips instead to be created, one 10MB and the other 9MB. However, when I give a .Length in the zip file inside my loop it says the size is 0. When I finish adding the files it says that is 19MB. Would anyone know how to do this?
I am using only System.IO.Compression to this task.
here is some example to show how I am trying
String FilePath = "D:\Invoices";
string[] oFiles = Directory.GetFiles(FilePath,"*.pdf");
string name = DateTime.Now.Ticks.ToString()+".zip";
using(FileStream archive1 = File.Open(name,FileMode.Create))
{
using(var arch = new ZipArchive(archive1,ZipArchiveMode.Update))
{
for(int i =0; i<oFiles.Length;i++)
{
var fileinf = new FileInfo(oFiles[i]);
arch.CreateEntryFromFile(fileinf.FullName,fileinf.Name);
//here the zip file is always 0
Console.WriteLine(archive1.Length);
}
}
}
//here the zip file is updated
From the documentation:
When you set the mode to Update … The content of the entire archive is held in memory, and no data is written to the underlying file or stream until the archive is disposed.
If you want to be able to read the size of the file as you're adding things, you need to use ZipArchiveMode.Create.
Otherwise, you should use the ZipArchiveEntry.CompressedSize property to monitor how much you've added to the archive.
I am trying to create a function that will retrieve all the uploaded files (which are now saved as byte in the database) and download it in a single zip file. I currently have 6000 files to download (and the number could grow).
The functionality is already working (from retrieval to download) if I limit the number of files being downloaded, otherwise, I get an OutOfMemoryException on the ForEach loop.
Here's a pseudo code: (files variable is a list of byte array and file name)
var files = getAllFilesFromDB();
foreach (var file in files)
{
var tempFilePath = Path.Combine(path, filename);
using (FileStream stream = new FileStream(tempfileName, FileMode.Create, FileAccess.ReadWrite))
{
stream.Write(file.byteArray, 0, file.byteArray.Length);
}
}
private readonly IEntityRepository<File> fileRepository;
IEnumerable<FileModel> getAllFilesFromDb()
{
return fileRepository.Select(f => new FileModel(){ fileData = f.byteArray, filename = f.fileName});
}
My question is, is there any other way to do this to avoid getting such errors?
To avoid this problem, you could avoid loading all the contents of all the files in one go. Most likely you will need to split your database call in to two database calls.
Retrieve a list of all the files without their contents but with some identifier - like the PK of the table.
A method which retrieves the contents of an individual file.
Then your (pseudo)code becomes
get list of all files
for each file
get the file contents
write the file to disk
Another possibility is to alter the way your query works currently, so that it uses deferred execution - this means it will not actually load all the files at once, but stream them one at a time from the database - but without seeing more code from your repository implementation, I cannot/ will not guess the right solution for you.
I build WPF program , And i use this code to save a List of objects to a File:
var list = new ArrayList();
list.Add("item1");
list.Add("item2");
// Serialize the list to a file
var serializer = new BinaryFormatter();
using (var stream = File.OpenWrite("test.dat"))
{
serializer.Serialize(stream, list);
}
And my problem is where to save this file on the disk. i read that i can't use the ProgramFiles Folder because sometimes only the admin user can save to this folder files.
There is any universal folder that i can use to save files?
I'd save it to Application Data. You can get it's path using
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
Is this data internal to your program, or user generated? If the data is internal, you probably want to use the Application Data folder. If it's user generated, you should probably default to My Documents, but let the user to decide where to save it.
You can call Environment.GetFolderPath() to get the location of these special folders.
When modifying a file, is it always necessary to rewrite the entire file or is it possible to find and change some small part of a file without having to rewrite the whole thing?
If you're not inserting or deleting data, then you don't need to rewrite the file. You will be replacing existing bytes with new values, or appending to the file.
If you need to insert or delete, you only need to rewrite the file from that point onwards. The only time you would need to rewrite the entire file is if you are inserting bytes at the beginning.
It's okay to open a file with both read and write permissions. That way you can search the file for whatever you're looking for, and once you have the position you can seek to it (from memory, the write pointer is separate from the read pointer) and overwrite data to your heart's content =)
If you're not changing the length of the data, you can always just seek to the appropriate position in the file, and write a new set of bytes. This replaces whatever bytes where originally there.
There are two possible ways:
when you use StreamWriter sw = new StreamWriter it will delete the data from file.
To change only, you can store the file data in a List<string>. This works nice for me:
List<string> lines_list = new List<string>();
int file_l = 0
StreamReader sr_temp = new StreamReader(_path);
string line;
while ((line = sr_temp.ReadLine()) != null)
{
lines_list.Add(line);
file_l++;
}
sr_temp.Close();
StreamWriter sw = new StreamWriter(_path);
for (int i = 0; i < file_l; i++)
{
sw.WriteLine(lines_list[i]);
}
//here you add some data
sw.Close();
Is there a way to calculate the checksum of a file that is readonly?
The only examples I have seen uses an algorithm like this
public string GetChecksum()
{
FileStream file = new FileStream(_filePath, FileMode.Open);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] retVal = md5.ComputeHash(file);
file.Close();
StringBuilder sb = new StringBuilder();
foreach (byte t in retVal)
{
sb.Append(retVal[1].ToString("x2"));
}
return sb.ToString();
}
You can open a file even if it's readonly.
It is not possible to generate a checksum without opening the file, since you can't read a file without opening it.
You should pass FileAccess.Read to open it as read-only.
Also, you should generate checksums using SHA512, not MD5.
According to the documentation, the FileStream constructor you are using opens the file for read/write. Use an overload that specifies FileAccess.Read.
The constructor is given read/write
access to the file, and it is opened
sharing Read access
You cannot generate a checksum without reading the entire file.
Generally, readonly files can be opened. There might be file or folder permissions that prevent a given user from opening the file.
Well, no. You have to read a file to do anything with what's in it. But you're opening with Generic access when you probably want FileStream(_filePath,FileAccess.Read,true,4096,true); to open it read-only. StreamReader will do this automatically.