I need help in how to lock the file, copy it, modify copy, and save copy back to original file, while the original file remains locked.
Here is the current flow:
User opens the file, the application copies it into a temporary folder and locks the original file with the following line
_lockStream = new FileStream(Path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
Then when it comes to save the temp file back to the original file, currently I am using the following routine (destination = _lockStream, and source = temp file's stream):
public static void Copy(Stream source, Stream destination, int bufferSize)
{
int readed = 0;
byte[] buffer = new byte[bufferSize];
destination.SetLength(source.Length);
destination.Position = 0;
while ((readed = source.Read(buffer, 0, buffer.Length)) > 0)
{
destination.Write(buffer, 0, readed);
}
destination.Flush();
}
I would prefer to use File.Copy instead, but I don't know how to make it working with locked file, even if file is locked by the application.
And, what's worse, my method it's not safe, if application or system crashes in the middle, the original file is corrupted. I think that more correct approach would be copy to the same folder, and then move (or just simply move) but I don't know how to do it retaining lock on the original file.
So, now to make it safe I release file lock, and use File.Copy, then obtain lock again. It works, but for some milliseconds the file is unlocked and can be locked by other processes.
What is best way of doing such things?
In order to make your method safe you can use TransactionScope like this:
using(var scope = new TransactionScope())
{
Copy(source, destination, buffersize);
scope.Complete();
}
Related
Here is my code:
enter image description here
using System.IO;
namespace Randoms
{
public class Program
{
public static void Main()
{
byte[] buffer = new byte[10240]; // buffer size
string path = #"C:\Users\RAHUL\Desktop\file.txt";
using (FileStream source = new FileStream(path, FileMode.Open, FileAccess.Read))
{
long fileLength = source.Length;
using (FileStream dest = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
{
long totalBytes = 0;
int currentBlockSize = 0;
while ((currentBlockSize = source.Read(buffer, 0, buffer.Length)) > 0)
{
totalBytes += currentBlockSize;
double percentage = (double)totalBytes * 100.0 / fileLength;
dest.Write(buffer, 0, currentBlockSize);
}
}
}
}
}
}
Please check the image which shows the error which I am getting.I have tried to change the FileAccess multiple times but not getting any luck.
This post explains how to both read and write to a file using a single stream:
How to both read and write a file in C#
Consider you may have the file still open from a previous erroneous run. Use a tool like Sysinternals Process Monitor or Unlocker to verify it isn't open by another instance.
How to use Process Monitor:
http://www.manuelmeyer.net/2013/09/tooltips-unlocking-files-with-sysinternals-process-monitor/
Both source and dest are referencing the same file. In the first instance (source) you open it exclusively (ie not shared).
In the second instance (dest) you now want to create the file which you opened in the first instance, but allow it to be shared.
Since the source is already open an in use you cannot write over the top of it using dest.
I think what you may be really want is to have the path parameter for the dest to be different to path parameter for the source, since you are essentially trying to re-write the same data into the same file at the same location right now.
I'm making a WP8.1 Silverlight app, it's basically done, but I have one big problem.
public void CheckAndCopyDBToIsoStore(params bool[] list)
{
// Obtain a reference to the application's isolated storage
IsolatedStorageFile iso = IsolatedStorageFile.GetUserStoreForApplication();
//Only copy db file over if it does not already exist
if (!iso.FileExists("TankolasKonyveloDB.sdf") || (list.Length == 1 && list[0] == true))
{
using (Stream input = Application.GetResourceStream(new Uri("TankolasKonyveloDB.sdf",
UriKind.Relative)).Stream)
{
using (IsolatedStorageFileStream output = iso.CreateFile("TankolasKonyveloDB.sdf"))
{
byte[] readBuffer = new byte[4096];
int bytesRead = -1;
// Copy the file from the install folder to isolated storage.
while ((bytesRead = input.Read(readBuffer, 0, readBuffer.Length)) > 0)
{
output.Write(readBuffer, 0, bytesRead);
}
output.Close();
output.Dispose();
iso.Dispose();
}
}
}
}
I have this method in App.xaml.cs, it is called once in the constructor on application start without parameters and it copies the empty database to the isostore if it's not already there. It's working well. The next thing I'd like to do is, that if I click a button I call this method again with a bool argument with a value of true. When I do this, I want to copy the empty database again over the existing in runtime.
At the line
IsolatedStorageFileStream output = iso.CreateFile("TankolasKonyveloDB.sdf")
I get an exception:
Operation not permitted on IsolatedStorageFileStream
I googled it and the answers were I must close and dispose the connection and everything, but the problem is still there. Instead of iso.CreateFile I tried OpenFile with FileMode.OpenOrCreate, Create and CreateNew... None of them worked.
So the thing is, I want to copy the .sdf file to the isostore if it doesn't already exist and I don't want to copy it again on every startup, unless I express that by clicking that button.
If somebody has a solution to this or an idea, please help me!
I'm attempting to use StreamReader and StreamWriter to grab a temporary output log (.txt format) from another application.
The output log is always open and constantly written to.
Unhelpfully if the application closes or crashes, the log file ends up deleted - hence the need for a tool that can grab the information from this log and save it.
What my program currently does is:
Create a new .txt file, and stores the path of that file as the
string "destinationFile".
Finds the .txt log file to read, and stores the path of that file as
the string "sourceFile"
It then passes those two strings to the method below.
Essentially I'm trying to read the sourceFile one line at a time.
Each time one line is read, it is appended to destinationFile.
This keeps looping until the sourceFile no longer exists (i.e. the application has closed or crashed and deleted its log).
In addition, the sourceFile can get quite big (sometimes 100Mb+), and this program may be handling more than one log at a time.
Reading the whole log rather than line by line will most likely start consuming a fair bit of memory.
private void logCopier(string sourceFile, string destinationFile)
{
while (File.Exists(sourceFile))
{
string textLine;
using (var readerStream = File.Open(sourceFile,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite))
using (var reader = new StreamReader(readerStream))
{
while ((textLine = reader.ReadLine()) != null)
{
using (FileStream writerStream = new FileStream(destinationFile,
FileMode.Append,
FileAccess.Write))
using (StreamWriter writer = new StreamWriter(writerStream))
{
writer.WriteLine(textLine);
}
}
}
}
}
The problem is that my WPF application locks up and ceases to respond when it reaches this code.
To track down where, I put a MessageBox just before the writerStream line of the code to output what the reader was picking up.
It was certainly reading the log file just fine, but there appears to be a problem with writing it to the file.
As soon as it reaches the using (FileStream writerStream = new FileStream part of the code, it stops responding.
Is using the StreamWriter in this manner not valid, or have I just gone and dome something silly in the code?
Am also open to a better solution than what I'm trying to do here.
Simply what I understand is you need to copy a file from source to destination which may be deleted at any time.
I'll suggest you to use FileSystemWatcher to watch for source file changed event, then just simply copy the whole file from source to destination using File.Copy.
I've just solved the problem, and the issue was indeed something silly!
When creating the text file for the StreamWriter, I had forgotten to use .Dispose();. I had File.Create(filename); instead of File.Create(filename).Dispose(); This meant the text file was already open, and the StreamWriter was attempting to write to a file that was locked / in use.
The UI still locks up (as expected), as I've yet to implement this on a new thread as SteenT mentioned. However the program no longer crashes and the code correctly reads the log and outputs to a text file.
Also after a bit of refinement, my log reader/writer code now looks like this:
private void logCopier(string sourceFile, string destinationFile)
{
int num = 1;
string textLine = String.Empty;
long offset = 0L;
while (num == 1)
{
if (File.Exists(sourceFile))
{
FileStream stream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using (new StreamReader(stream))
{
stream.Seek(offset, SeekOrigin.Begin);
TextReader reader2 = new StreamReader(stream);
while ((textLine = reader2.ReadLine()) != null)
{
Thread.Sleep(1);
StreamWriter writer = new StreamWriter(destinationFile, true);
writer.WriteLine(textLine);
writer.Flush();
writer.Close();
offset = stream.Position;
}
continue;
}
}
else
{
num = 0;
}
}
}
Just putting this code up here in case anyone else is looking for something like this. :)
lets say there is a Function
public void UploadSomewhere(FileStream fs)
If I use FileStream Its not enough to point to the file path, I need also to select the FileMode and Im not sure what is the correct FileMode if you are just copy/moving the file. Any ideas?
If you want to use FileStream you can simply use Open for the source file.
e.g.,
var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
For the target file you have to decide what happens if it exists:
Create will let you overwrite an existing file if you've got the Write permission.
CreateNew will throw if the file exists. Again, you'd need the Write permission.
But, as others have written, you could skip the FileStream completely and use the File commands.
Reference: http://msdn.microsoft.com/en-us/library/system.io.filemode.aspx
If you're just coping/moving file, and you know already path (as much as I understand from the question), you do not need FieStream at all. It's enough to use File.Move or File.Copy functions.
See http://msdn.microsoft.com/en-us/library/system.io.filemode.aspx
If you only need to read the file, I suggest FileMode.Open
If you are copying the file FileMode.Open and FileAccess.Read.
If you want to share access to the file during this process, specify the FileShare option as well.
Take a look at the MSDN for more information.
If you want to copy a file using FileStream the example below should point you to the right way.
using (FileStream sourceFile = new FileStream("c:\\pathToSourceFile", FileMode.Open,FileAccess.Read))
{
using (FileStream destFile = new FileStream("c:\\pathToDestinationFile", FileMode.Create))
{
int bufferSize = 65536;
int bytesRead = -1;
byte[] bytes = new byte[bufferSize];
while ((bytesRead = sourceFile.Read(bytes, 0, bufferSize)) > 0)
{
destFile.Write(bytes, 0, bytesRead);
}
}
}
Hi
Does anyone have workable sample code that copy files and support resuming them if there are the source has been disconnected?
In this example I am copying videos files. If the source is disconnected ie usb was unplugged, how can I support resuming them again? I have tried some code on stackoverflow, but after resuming, the video files seem to be corrupted. Is FileStream the best solution for video transfers/resume?
Any other pointers or tips are welcome.
private void CreateNewCopyTo(FileStream source, FileStream dest) {
int size = (source.CanSeek) ? Math.Min((int)(source.Length - source.Position), 0x2000) : 0x2000;
byte[] buffer = new byte[size];
int read;
long fileLength = source.Length;
while ((read = source.Read(buffer, 0, buffer.Length)) > 0) {
dest.Write(buffer, 0, read);
dest.Close();
dest = new FileStream(dest.Name, FileMode.Append, FileAccess.Write);
}
dest.Close();
}
private void ResumeCopyTo(FileStream source, FileStream dest) {
int size = (source.CanSeek) ? Math.Min((int)(source.Length - source.Position), 0x2000) : 0x2000;
byte[] buffer = new byte[size];
long TempPos = source.Position;
while (true) {
int read = source.Read(buffer, 0, buffer.Length);
if (read <= 0)
return;
dest.Write(buffer, 0, read);
dest.Close();
dest = new FileStream(dest.Name, FileMode.Append, FileAccess.Write);
}
}
Try to insert source.Seek(dest.Length, SeekOrigin.Begin); as first line in ResumeCopyTo method.
create a file that is the same size as the file your copying. And if the connection is broken write a "BookMark" into the file. Some arbitrary string at the end of the file that tells you where you left off.
Best to get a hash of the original file to determine if the file has changed since the last copy attempt. (if the file is different then you should check the data in the destination with the data in the source, perhaps with another hash of the data.)
The most important part tho is to seek in the source and the destination to the exact same point in both files. before the resume attempt.
You will want to make sure that your seek will give you the next byte in the source file. Not the same byte you left off at. Otherwise you will be left with your data having a single duplicate character. Which will corrupt the data.
(e.g. if the source file is 12345678901234567890 and your destination made it to 123456 , you dont want to resume at 6, you want to resume at 7. otherwise your destination would be 123456678901234567890)
Assuming that the file does not change in time, we can use the functions that you have written. However, they should be slightly modified and simplified to one function.
private void ResumeCopy(FileStream source, FileStream dest)
{
byte[] buffer = new byte[0x10000]; //use 64kB buffer
int read;
source.Seek(dest.Length, SeekOrigin.Begin);
while ((read = source.Read(buffer, 0, buffer.Length)) > 0)
{
dest.Write(buffer, 0, read);
dest.Flush();
}
}
We can create an additional function:
private void ResumeCopyTo(string sourceFilePath, string destFilePath)
{
FileStream input = new FileStream(sourceFilePath, FileMode.Open, FileAccess.Read);
FileStream output = new FileStream(destFilePath, FileMode.Append, FileAccess.Write);
ResumeCopy(input, output);
output.Close();
input.Close();
}
Ideas for a production environment:
add error handling
check whether the file has changed (use checksum)
opened the source file in exclusive mode
BTW:
In this way, I copied an 15 GB file in 9 hours via a network connection of really poor quality.