Zip file being accessed by multiple user - Mem out of exception issue - c#

So this is what I have so far
in my view
#foreach (var file in item.AssociatedFileList)
{
#Html.ActionLink(file.FileName, "GetFile", "Landing", new { path = file.FilePath }, new { target = "_blank" }) <br /><br />
}
and my controller
public ActionResult GetFile(string path)
{
var extension = Path.GetExtension(path);
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
if (extension == ".pdf")
return File(fs, "application/pdf", Path.GetFileNameWithoutExtension(path));
if (extension == ".xlsx" || extension == ".xls")
return File(fs, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", Path.GetFileNameWithoutExtension(path));
if (extension == ".zip")
return File(fs, "application/zip", Path.GetFileNameWithoutExtension(path));
return this.Content("<html><center><span style='font-size: large; font-weight: bold; font-family: 'Century Gothic', 'PT Sans', 'Verdana', 'Arial', 'Helvetica', 'sans-serif'; color: #FF0000;'>There is no file to view<span></center></html>");
}
The logic behind displaying the file is ruled by case and sub case
1 case - multiple sub case.
Each subcase is represented by different file types(pdf, excel or zip)
the directory name is case name and file is the directory is subcase with appropriate extension.
to sum it up
C drive has a folder name my application -> it has a account number folders -> each account has miltiple case folder click on one case say CASE_1 folder -> its has SUBCASE_1.pdf , SUBCASE_2.xlsx , SUBCASE_3.zip etc files
Now each file is displayed on screen independently note: the files have to be displayed independently not zipped at directory(case) level. when user clicks the action link they can read or download.
#Html.ActionLink(file.FileName, "GetFile", "Landing", new { path = file.FilePath }, new { target = "_blank" }) <br /><br />
These zip files can be huge
the problem i am facing is when only one user is trying to access the file everything works fine but if more than one user tries to access the file I get
Server Error in '/' Application.
Exception of type 'System.OutOfMemoryException' was thrown.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.]
System.IO.MemoryStream.set_Capacity(Int32 value) +93
System.IO.MemoryStream.EnsureCapacity(Int32 value) +64
System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count) +330
Microsoft.VisualStudio.Web.PageInspector.Runtime.Tracing.ArteryFilter.Write(Byte[] buffer, Int32 offset, Int32 count) +106
System.Web.HttpWriter.FilterIntegrated(Boolean finalFiltering, IIS7WorkerRequest wr) +9509748
System.Web.HttpResponse.FilterOutput() +104
System.Web.CallFilterExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +49
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +69
later I checked the file size of filestream oibject it is 108560843
I am putting everything I can think of in the question. this is my first time working with files and I don't want to miss any detail.

Related

System.IO.IOException: Cannot create a file when that file already exists. Even after deleting the existing file

Using .NET Core 3.1 and C#, I'm trying to move a directory (including all subdirectories and files) to another directory. The destination directory may contain folders and files that already exist with the same name as the source directory, for example "source/folder/file.txt" may already exist in "destination/folder/file.txt" but I would like to overwrite everything in the destination directory.
The error I am getting is "System.IO.IOException: Cannot create a file when that file already exists.", however I am deleting the file that already exists in the destination before moving the file from the source (File.Delete before File.Move), so I don't understand why I am getting this error. Also to add, I am not able to reproduce this error 100% of the time for some reason.
This is the code I am using to move a directory (lines 137 - 155):
public static void MoveDirectory(string source, string target)
{
var sourcePath = source.TrimEnd('\\', ' ');
var targetPath = target.TrimEnd('\\', ' ');
var files = Directory.EnumerateFiles(sourcePath, "*", SearchOption.AllDirectories)
.GroupBy(s => Path.GetDirectoryName(s));
foreach (var folder in files)
{
var targetFolder = folder.Key.Replace(sourcePath, targetPath);
Directory.CreateDirectory(targetFolder);
foreach (var file in folder)
{
var targetFile = Path.Combine(targetFolder, Path.GetFileName(file));
if (File.Exists(targetFile)) File.Delete(targetFile);
File.Move(file, targetFile);
}
}
Directory.Delete(source, true);
}
This is the stack trace of my error:
Description: The process was terminated due to an unhandled exception.
Exception Info: System.IO.IOException: Cannot create a file when that file already exists.
at System.IO.FileSystem.MoveFile(String sourceFullPath, String destFullPath, Boolean overwrite)
at Module_Installer.Classes.Bitbucket.MoveDirectory(String source, String target) in F:\git\module-installer\module-installer\Module Installer\Classes\Bitbucket.cs:line 147
at Module_Installer.Classes.Bitbucket.DownloadModuleFiles(Module module, String username, String password, String workspace, String repository, String commitHash, String versionNumber, String downloadDirectory, String installDirectory) in F:\git\module-installer\module-installer\Module Installer\Classes\Bitbucket.cs:line 113
at Module_Installer.Classes.OvernightInstall.ProcessInstalledModule(TenantModule tenantModule, Boolean skipBackup) in F:\git\module-installer\module-installer\Module Installer\Classes\OvernightInstall.cs:line 393
at Module_Installer.Classes.OvernightInstall.Run(Boolean skipBackup) in F:\git\module-installer\module-installer\Module Installer\Classes\OvernightInstall.cs:line 75
at Module_Installer.Program.Main(String[] args) in F:\git\module-installer\module-installer\Module Installer\Program.cs:line 40
This error is happening when I am running the application via Windows Task Scheduler, which I have set to run at 03:30am every day, I have specified that the task should "Start In" the same folder as where the EXE is located.
Any suggestions would be appreciated, thanks!
Instead of deleting existing files in the target directory try to overwrite them using File.Move(file, targetFile, overwrite: true).
By the way there is an MSDN example on how to copy directories. It's not exactly your use case, but could be helpful anyway.

IText 7 in C# locking bad pdf

I am running into a problem where I am using IText 7 to check a PDF that a user has downloaded off the internet.
For my test case I created a text file with garbage in it and saved it as a pdf. I know its not valid.
In the code I am trying to open the PDF using PDFReader.
An exception is being thrown, this is expected.
When debugging the code the Reader object is null when it gets to the finally spot. So the
reader.close() isn't even firing.
I am even copying the file to a temp directory just to ensure nothing else is holding the file.
I am then unable to delete the PDF file either in code or manually in a file explorer after the exception.
Here is some of my code. I removed everything but the Reader part. Also this code is after I have tried a few things, so you are seeing my attempt with the file being copied to a temp file. I am attempted to delete the temp file in the finally part. That is failing on a corrupt file.
Here are both the exceptions that are thrown when attempting to validate a bad PDF. The first is from the PDFReader call.
2021-04-09 13:18:11,079 ERROR GUI.Form1 - PDF header not found.
iText.IO.IOException: PDF header not found. at
iText.IO.Source.PdfTokenizer.GetHeaderOffset() at
iText.Kernel.Pdf.PdfReader.GetOffsetTokeniser(IRandomAccessSource> byteSource) at
iText.Kernel.Pdf.PdfReader..ctor(String filename, ReaderProperties properties) at
iText.Kernel.Pdf.PdfReader..ctor(FileInfo file) at
GUI.Form1.validatePDF(FileInfo pdfFile, HashSet`1 tmpMd5s)
The Second is from the attempt to delete the temp file
2021-04-09 13:18:11,116 ERROR GUI.Form1 - The process cannot access the file
'C:\Users\ret63\AppData\Local\Temp\tmp27DE.tmp' because it is being used by another process.
System.IO.IOException: The process cannot access the file 'C:\Users\ret63\AppData\Local\Temp\tmp27DE.tmp' because it is being used by another process. at
System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.FileInfo.Delete() at
GUI.Form1.validatePDF(FileInfo pdfFile, HashSet`1 tmpMd5s)
PdfDocument pdfDoc = null;
PdfReader reader = null;
try
{
using (reader = new PdfReader(testFile))
{
//pdfDoc = new PdfDocument(reader);
//pdfDoc = new PdfDocument(new PdfReader(pdfFile.FullName));
//Console.WriteLine("Number of Pages: " + pdfDoc.GetNumberOfPages());
//pdfDoc.Close();
}
}
catch(Exception ex)
{
log.Error(ex.Message, ex);
throw new Exception("Invalid PDF File: " + pdfFile.Name);
}
finally
{
if (reader != null)
{
reader.Close();
}
if (pdfDoc != null && !pdfDoc.IsClosed())
{
pdfDoc.Close();
}
try
{
if (testFile.Exists)
{
testFile.Delete();
}
}
catch (Exception ee)
{
Console.WriteLine(ee.Message);
}
}
Looks like an iText bug. If you trace out what gets called by the PdfReader constructor, you see that it creates a FileStream that is conditionally locked. The FileStream gets wrapped in a RandomAccessSource which is then wrapped in a PdfTokenizer in GetOffsetTokeniser. If GetHeaderOffset throws on line 1433, that tok local is never closed.

File.Replace throws ERROR_UNABLE_TO_MOVE_REPLACEMENT

I have a 'safe' config file saving routine that tries to be atomic when writing user config data to the disk, avoiding disk caching etc.
The code goes something like this:
public static void WriteAllTextSafe(string path, string contents)
{
// generate a temp filename
var tempPath = Path.GetTempFileName();
// create the backup name
var backup = path + ".backup";
// delete any existing backups
if (File.Exists(backup))
File.Delete(backup);
// get the bytes
var data = Encoding.UTF8.GetBytes(contents);
if (File.Exists(path))
{
// write the data to a temp file
using (var tempFile = File.Create(tempPath, 4096, FileOptions.WriteThrough))
tempFile.Write(data, 0, data.Length);
// replace the contents
File.Replace(tempPath, path, backup, );
}
else
{
// if the file doesn't exist we can't replace so just write it
using (var tempFile = File.Create(path, 4096, FileOptions.WriteThrough))
tempFile.Write(data, 0, data.Length);
}
}
On most systems this works perfectly and I have not had any reports of issues, but for some users each time my program calls this function they get the following error:
System.IO.IOException: 置換するファイルを置換されるファイルに移動できません。置換されるファイルの名前は、元のままです。
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.File.InternalReplace(String sourceFileName, String destinationFileName, String destinationBackupFileName, Boolean ignoreMetadataErrors)
at download.ninja.BO.FileExtensions.WriteAllTextSafe(String path, String contents)
at download.ninja.BO.FileExtensions.SaveConfig(String path, Object toSave)
After a bit of investigation and with the help of Google Translate I've found that the actual error is thrown from the wine32 ReplaceFile function:
ERROR_UNABLE_TO_MOVE_REPLACEMENT 1176 (0x498)
The replacement file could not be renamed. If lpBackupFileName was specified, the replaced and
replacement files retain their original file names. Otherwise, the replaced file no longer exists
and the replacement file exists under its original name.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365512(v=vs.85).aspx
The problem is that I have no idea why Windows is throwing this error.. I have tried setting the file to readonly locally but that throws an Unauthorized exception rather than a IOException so I don't believe that is causing the problem.
My second guess is that the the is somehow locked, but I only have one read function that is used for reading all config files and that should be closing off all file handles when it finsihes
public static T LoadJson<T>(string path)
{
try
{
// load the values from the file
using (var r = new StreamReader(path))
{
string json = r.ReadToEnd();
T result = JsonConvert.DeserializeObject<T>(json);
if (result == null)
return default(T);
return result;
}
}
catch (Exception)
{
}
return default(T);
}
I have also tried throwing fake exceptions in the LoadJson function to try and lock the file but I can't seem to do it.
Even then I have tried simulating a file lock by opening the file in a different process and running the save code while it is still open, and that generates a different (expected) error:
System.IO.IOException: The process cannot access the file because it is being used by another process.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.File.InternalReplace(String sourceFileName, String destinationFileName, String destinationBackupFileName, Boolean ignoreMetadataErrors)
at download.ninja.BO.FileExtensions.WriteAllTextSafe(String path, String contents)
So the question is.. what is causing Windows to throw this ERROR_UNABLE_TO_MOVE_REPLACEMENT error on some systems
NOTE: This error is thrown EVERY TIME my program attempt to replace the file on an affected machine.. not just occasionally.
OK, so found the problem. It seems that files passed into ReplaceFile must be on the SAME DRIVE
In this case the user had changed their temp folder to d:\tmp\
So File.Replace looked something like this:
File.Replace(#"D:\tmp\sometempfile", #"c:\AppData\DN\app-config", #"c:\AppData\DN\app-config.backup");
This difference in drives between the source file and the desitnation file in File.Replace seems to be what caused the problem.
I have modified my WriteAllTextSafe function as follows and my user reports the problem has been resolved!
public static void WriteAllTextSafe(string path, string contents)
{
// DISABLED: User temp folder might be on different drive
// var tempPath = Path.GetTempFileName();
// use the same folder so that they are always on the same drive!
var tempPath = Path.Combine(Path.GetDirectoryName(path), Guid.NewGuid().ToString());
....
}
As far as I can find there is no documentation stating that the two files need to be on the same drive but I can reproduce the problem by manually entering different drives (yet valid paths) into File.Replace
Are you by any chance, hardcoding drives in your file name, e.g. c:\ etc?
If you are, don't, they might not not work in runtimes where the locale is 1041 (japanese).
Instead use the framework to get the drive part and dynamically build your path.
string drivePath = System.IO.Path.GetPathRoot(System.Environment.SystemDirectory);
string somePath = string.Concat(drivePath, "someFolder\SomeFileName.Txt");
I've had the same problem working with source files on OneDrive copying to local files under %AppData%. A solution that worked well for me was to delete the destination file then perform a copy from the source.
Code that does not work:
file.replace(source,destination,backup,false)
Code that does work:
File.Delete(destination)
File.Copy(source, destination)

Exception Details: System.Web.HttpException: Maximum request length exceeded

Am trying to read data from an Excel file into an ADO.NET Dataset using the code below. In a windows Forms application it work, but in an asp.net application it fails.
public static DataTable ArchiveData(string fileName)
{
FileStream stream = File.Open(fileName, FileMode.Open, FileAccess.Read);
//Reading from a OpenXml Excel file (2007 format; *.xlsx)
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
excelReader.IsFirstRowAsColumnNames = true;
DataSet result = excelReader.AsDataSet();
//Free resources (IExcelDataReader is IDisposable)
excelReader.Close();
return result.Tables["Archive data"];
}
Stack Trace:
[HttpException (0x80004005): Maximum request length exceeded.]
System.Web.HttpRequest.GetEntireRawContent() +8793522
System.Web.HttpRequest.GetMultipartContent() +62
System.Web.HttpRequest.FillInFormCollection() +236
System.Web.HttpRequest.get_Form() +68
System.Web.HttpRequest.get_HasForm() +8745879
System.Web.UI.Page.GetCollectionBasedOnMethod(Boolean dontReturnNull) +97
System.Web.UI.Page.DeterminePostBackMode() +63
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +133
OR is there a better way of reading an Excel file from a client machine into an ADO.NET DataTable in ASP.NET
Add following tag in your web.config file and check if it works
<httpRuntime maxRequestLength="350000" enableVersionHeader="false" maxQueryStringLength="3584" executionTimeout="600"/>

StreamWriter generates 0 byte file

I have the following logics below that get called once every 20 minutes via a Timer, it serializes the content of the object into a file path, The filePath i see is \hard disk\logs\applicationstate.xml , please note I confirm this is a valid path..
It works most of the time but every now and then I get the System.IO.IOeException on the line this.StreamWriter = new StreamWriter(filePath); with the following stack stack trace:
at System.IO.__Error.WinIOError(Int32 errorCode, String str)\r\n at
System.IO.FileStream..ctor(String path, FileMode mode, FileAccess
access, FileShare share, Int32 bufferSize, Boolean useAsync, String
msgPath)\r\n at System.IO.FileStream..ctor(String path, FileMode
mode, FileAccess access, FileShare share, Int32 bufferSize)\r\n at
System.IO.StreamWriter.CreateFile(String path, Boolean append)\r\n
at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding
encoding, Int32 bufferSize)\r\n at
System.IO.StreamWriter..ctor(String path)\r\n at
Shs.ScanPanel.CA.DataManager.DataManagercr.CopyData(Object data)\r\n
at System.Threading.Timer.ring()\r\n"
When it happens I see \hard disk\logs\applicationstate.xml exists but it has 0 byte.
So my question is, could the StreamWriter cause this 0 byte file to be generated in the first place? I read up on the IOException under StreamWriter on MSDN and it says the following
IOException
path includes an incorrect or invalid syntax for file name, directory name, or volume label syntax.
This confused me, is it because it tries to open a stream writer to a 0 byte file? could this 0 byte generated in the last time this code runs where a null object was being serialized into the file? if so why didn't I see that exception in Visual Studio?
if (filePath != string.Empty)
{
if (this.StateObject == null)
{
this.StateObject = new State();
}
//Do something to my StateObject object
this.StreamWriter = new StreamWriter(filePath);
this.Serializer = new XmlSerializer(typeof(State));
this.Serializer.Serialize(this.StreamWriter, this.StateObject);
}
else
{
if (this.log != null)
{
this.log.Write(LogLevel.Error, this.componentName, "CopyData : Unable to initilize State Object");
}
}
}
else
{
if (this.log != null)
{
this.log.Write(LogLevel.Error, this.componentName, "CopyData : Error while retrieving Current working directory");
}
}
}
catch (Exception ex)
{
if (this.log != null)
{
this.log.Write(ex, this.componentName);
}
}
finally
{
if (this.StreamWriter != null)
{
this.StreamWriter.Close();
}
}
I recommend using this.StreamWriter.Flush() to make sure all the contents are written.
However you exception appears to be complaining that the path is incorrect.
Edit: Opps I missed the WinCE tag
So I wrote up a little program and I confirmed that the line that makes the file to have 0 byte is right at the this.StreamWriter = new StreamWriter(filePath);
But what really boggle my mind is that it successfully wipe out the file so that new data can be serialized into it but yet at the same time it throws exception. I'm thinking this is a lower layer of the StreamWriter api or it could be something with the flash drive.... after all I'm running this program on WINDOW CE

Categories

Resources