I am copying files asynchronously with the article Microsoft provided https://learn.microsoft.com/en-us/dotnet/standard/io/asynchronous-file-i-o
The issue I am running into using this is that when the files are finished copying, it doesn't keep the date modified value and is set to the time the file was created.
To compensate for that, I am trying to set the date modified time for each file after their finished copying with the File.SetLastWriteTime static method.
foreach (var file in dir.EnumerateFiles())
{
string temppath = Path.Combine(destDirName, file.Name);
using (FileStream reader = new FileStream(file.FullName, FileMode.Open, FileAccess.Read))
{
using (FileStream writer = new FileStream(temppath, FileMode.Create, FileAccess.ReadWrite))
{
await reader.CopyToAsync(writer);
File.SetLastWriteTime(temppath, file.LastWriteTime);
}
}
}
Unfortunately, it seems that the File.SetLastWriteTime method executes immediately before await reader.CopyToAsync(writer) has finished.
How can I make sure that the File.SetLastWriteTime method isn't executed until after reader.CopyToAsync has finished?
It appears to work as intended if I change the method to copy synchronously within a Task.Run, but not sure if that is the correct way to do it.
I was able to figure it out.
The reason why it couldn't set the file time is because it was still within the stream.
I simply moved the method outside of the write stream and that resolved the problem.
foreach (var file in dir.EnumerateFiles())
{
string temppath = Path.Combine(destDirName, file.Name);
using (FileStream reader = new FileStream(file.FullName, FileMode.Open, FileAccess.Read))
{
using (FileStream writer = new FileStream(temppath, FileMode.Create, FileAccess.ReadWrite))
{
await reader.CopyToAsync(writer);
}
File.SetLastWriteTime(temppath, file.LastWriteTime);
}
}
Related
It's like that that right now I'm trying to open my file with FileStream where I saw a little further into the code to use streamwriter compared to writing it into the file every time it goes through Streamwriter.
When it runs through the first time then do it without any problems but as soon as I run it through the second lap. then it fails where it then writes "Stream was not writable"
int count = 0;
using (FileStream fs = new FileStream(#"C:\jpe\Projekt\Utilities\Icons\Icons/WriteLines.txt", FileMode.Append, FileAccess.Write))
{
foreach (SPSite tmpSite in tmpRootColl)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(string.Format("Title {0}", tmpSite.RootWeb.Title));
//Enumerate through each sub-site
foreach (SPWeb tmpWeb in tmpSite.AllWebs)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(string.Format("Title {0}", tmpWeb.Title));
//Enumerate through each List
foreach (SPList tmpList in tmpWeb.Lists)
{
if (tmpList.BaseTemplate == SPListTemplateType.DocumentLibrary)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(string.Format("Title {0}", tmpList.Title));
using (StreamWriter outputFile = new StreamWriter(fs)) //Errors come here when it runs the second round through.
{
await outputFile.WriteLineAsync($"{tmpSite.RootWeb.Title} - {tmpList.Title} {count}");
}
count++;
}
}
}
Console.WriteLine("__________________________________________________");
}
}
What I want to achieve with this is that it has to insert text into the file every time it runs through StreamWriter. It should not first make it to the last when it is finished.
i have read:
https://stackoverflow.com/a/7306243/18055701
C# how to update file(leave old data)
Currently you're creating a StreamWriter, writing to it, and disposing it for every list, this is what's causing the issue. Internally the Dispose method closes the underlying stream causing the exception. To solve this we can do one of 2 things
Tell our StreamWriter to not close the underlying stream.
Not dispose our StreamWriter until we're also done with the underlying stream.
Here's how to do #1:
Simply replace your call to the constructor with this
using (StreamWriter outputFile = new StreamWriter(fs, leaveOpen: true))
Here's how to do #2:
Move the using (StreamWriter ... block up to be "one level deeper" than the using (FileStream ... block
using (FileStream fs = new FileStream("..."))
{
using (StreamWriter outputFile = new StreamWriter(fs))
{
// Your foreach loops here
}
}
Personally I'd go with #2 as I'm not a fan of creating and disposing objects in a loop
Assuming you are using at least .NET framework 4.5.
The StreamWriter closes the base stream in its Dispose() method. You can adjust that behavior by using another construcor: https://learn.microsoft.com/en-us/dotnet/api/system.io.streamwriter.-ctor?view=netcore-3.1#system-io-streamwriter-ctor(system-io-stream-system-text-encoding-system-int32-system-boolean)
I'm trying to serve a file to the users that is packed inside a zip archive on the server.
The project is ASP.NET Core 5.0 MVC project.
I managed to do it by using ZipArchiveEntry.Open() and copying that to a memory stream.
string zipFile = #"D:\all_installs.zip";
using (FileStream fs = new FileStream(zipFile, FileMode.Open))
{
using (ZipArchive zip = new ZipArchive(fs))
{
ZipArchiveEntry entry = zip.Entries.FirstOrDefault(x => x.FullName == "downloadable file.iso");
string name = entry.FullName;
string baseName = Path.GetFileName(name);
//open a stream to the zip entry
Stream stream = entry.Open();
//copy stream to memory
MemoryStream memoryStream = new MemoryStream();
stream.CopyTo(memoryStream); //big memory usage?
memoryStream.Position = 0;
return this.File(memoryStream, "application/octet-stream", baseName);
}
}
This would require a lot of RAM if there are many simultaneous downloads, so instead I would like to serve it directly from the archive, which I know will require the CPU while unpacking it, but that's fine since the download speed will be very limited anyways.
I tried serving stream directly, but I get the following error:
NotSupportedException: Stream does not support reading.
How can I serve the entry-stream directly?
Problem is both FileStream fs and ZipArchive zip are disposed here, so when it's time to write response and asp.net tries to read your zip entry (stream) - it's not available any more, since everything has been disposed.
You need to not dispose them right away but instead tell asp.net to dispose them when it's done writing the response. For that, HttpResponse has method RegisterForDispose, so you need to do something like that:
string zipFile = #"C:\tmp\record.zip";
FileStream fs = null;
ZipArchive zip = null;
Stream stream = null;
try {
fs = new FileStream(zipFile, FileMode.Open);
zip = new ZipArchive(fs);
ZipArchiveEntry entry = zip.Entries.First(x => x.FullName == "24fa535b-2fc9-4ce5-96f4-2ff1ef0d9b64.json");
string name = entry.FullName;
string baseName = Path.GetFileName(name);
//open a stream to the zip entry
stream = entry.Open();
return this.File(stream, "application/octet-stream", baseName);
}
finally {
if (stream != null)
this.Response.RegisterForDispose(stream);
if (zip != null)
this.Response.RegisterForDispose(zip);
if (fs != null)
this.Response.RegisterForDispose(fs);
}
Now asp.net will first write the response, then dispose all your disposables for you.
I have a WCF service which works in raw format, using streams:
[ServiceContract]
public interface IEncryptingService
{
[WebInvoke(UriTemplate = "/")]
[OperationContract]
Stream SignDocument(Stream requestStream);
}
public class EncryptingService : IEncryptingService
{
public Stream SignDocument(Stream requestStream)
{
string originalFileName = Path.GetTempFileName();
string signedFileName = Path.GetTempFileName();
using (var originalFileStream = File.Open(originalFileName, FileMode.Create, FileAccess.Write))
{
requestStream.CopyTo(originalFileStream);
}
XmlDocumentSigner.SignFile(originalFileName, signedFileName);
return File.Open(signedFileName, FileMode.Open, FileAccess.Read);
}
}
Now, how can I remove this file after WCF ends returning the file?
I have tried to use finally block, but it gets called right after return, and throws exception, since the file is still used by a process.
Of course, these is a workaround like a background worker waiting for a file to be available for deletion, but, in my opinion, it is not like how web-services should be implemented.
I haven't tried it but you could open the file and write that stream to another stream. Something like:
MemoryStream ms = new MemoryStream();
using (FileStream fs = File.OpenRead(signedFileName))
{
//Read from fs and write to ms
}
Then all you will need to do is call delete on the file and return ms:
File.Delete(signedFileName);
return ms;
The solution came to mind inexcusably quickly, and it is absolutely logical: I can simply read file contents to memory and remove the file.
public Stream SignDocument(Stream requestStream)
{
string originalFileName = Path.GetTempFileName();
string signedFileName = Path.GetTempFileName();
using (var originalFileStream = File.Open(originalFileName, FileMode.Create, FileAccess.Write))
{
requestStream.CopyTo(originalFileStream);
}
XmlDocumentSigner.SignFile(originalFileName, signedFileName);
byte[] signedFileBytes = File.ReadAllBytes(signedFileName);
File.Delete(signedFileName);
return new MemoryStream(signedFileBytes);
}
Note that using statement makes this code fail as well:
using (var ms = new MemoryStream(signedFileBytes))
{
return ms;
}
I have downloaded a zip file from blob storage and save it to isolated storage of windows phone like this :- FileStream fs is downloaded from blob.
public static void SaveToIsolatedStorage(FileStream fs, string fileName)
{
var isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
using (var streamWriter =
new StreamWriter(new IsolatedStorageFileStream(fileName,
FileMode.Create,
FileAccess.ReadWrite,
isolatedStorage)))
{
streamWriter.Write(fs);
}
}
But when checked this zip file using IsoStoreSpy it is showing corrupted. I have checked it by reading from isolated storage and tried to unzip it but not working. I am sure that it is corrupted because when i replace this file using IsoStoreSpy with some other zip and then tried to unzip it then it is working.
Edit:-
Code for downloading from Blob
private async Task DownloadFileFromBlobStorage()
{
var filename = "AppId_2.zip";
var blobContainer = GetBlobClient.GetContainerReference("testwpclientiapcontainer");
var blob = blobContainer.GetBlockBlobReference(filename);
using (var filestream = new FileStream(filename, FileMode.Create))
{
await blob.DownloadToStreamAsync(filestream);
SaveToIsolatedStorage(filestream, filename);
}
}
So anybody know how can i save the zip file to isolated storage without getting it corrupted ?
You're using a StreamWriter. That's for text. You shouldn't be using it to copy a zip file at all. Never use any TextWriter for binary data.
Next you're using StreamWriter.Write(object), which is basically going to call ToString on the FileStream. That's not going to work either.
You should just create an IsolatedStorageStream, and then call fs.CopyTo(output).
public static void SaveToIsolatedStorage(Stream input, string fileName)
{
using (var storage = IsolatedStorageFile.GetUserStoreForApplication())
{
// Simpler than calling the IsolatedStorageFileStream constructor...
using (var output = storage.CreateFile(fileName))
{
input.CopyTo(output);
}
}
}
In your edit you've shown code which saves to a FileStream first, and then copies the stream from the current position. As you've noted in comments, you needed to rewind it first.
Personally I wouldn't use a FileStream at all here - why do you want to save it as a normal file and an isolated file? Just use a MemoryStream:
using (var stream = new MemoryStream())
{
await blob.DownloadToStreamAsync(filestream);
stream.Position = 0;
SaveToIsolatedStorage(stream, filename);
}
(Note that your SaveToIsolatedStorage method is still synchronous... you may wish to consider an asynchronous version.)
public void WriteListToFile(Lists lists, string filePath)
{
FileStream outFile;
BinaryFormatter bFormatter = new BinaryFormatter();
// Ppen file for output
outFile = new FileStream(filePath, FileMode.Create, FileAccess.Write);
// Output object to file via serialization
bFormatter.Serialize(outFile, lists);
// Close file
outFile.Close();
}
Whenever I try to output data to a .dat file I get an error saying that the file is already in use. How do I fix this?
EDT: Turns out it wouldn't let me save to an empty file so I create a new void to input data and then it allowed me to save over the file.
The immediate answer is "release the lock that some process has on the file".
Something already has the file open. You need to look at code and other processes that may access that file to find the root cause.
I note that you're not making use of using statements. If an exception were thrown in the block of code you show, outputFile.Close() would never execute, leaving the file open.
Try rewriting your code (and any similar code) like
public void WriteListToFile(Lists lists, string filePath)
{
BinaryFormatter bFormatter = new BinaryFormatter();
// Ppen file for output
using (FileStream outFile = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
// Output object to file via serialization
bFormatter.Serialize(outFile, lists);
// Close file
outFile.Close();
}
}
The using keyword is a syntactic shortcut for
var outFile = new FileStream(filePath, FileMode.Create, FileAccess.Write);
try
{
// Do stuff with outFile
}
finally
{
outFile.Dispose();
}
and ensures that outFile is disposed (which also closes it) whether or not an exception is thrown.
you can try this:
outFile.Dispose();