ZipArchive problem with Access to the process - c#

I try to do a function which of the files from the zip file will take two values ​​and update them in the GUI.
the problem is that i get exception about access to the file.
System.IO.IOException: 'The process cannot access the file 'C:\work\RasMol.zip' because it is being used by another process.'
public void ReplaceEntry(string fileName)
{
var infoList = new List<RPPXFileEntry>();
{
ZipArchive Archive = ZipFile.Open(this.sourcePath, ZipArchiveMode.Update);
//exception in this place
foreach (ZipArchiveEntry entry in Archive.Entries)
{
var rppxEntry = new RPPXFileEntry();
if (entry.Name == fileName)
{
rppxEntry.Modify = entry.LastWriteTime;
rppxEntry.Size = entry.Length;
infoList.Add(rppxEntry);
}
}
this.AllEntries = infoList;
}
}
someone know how to deal with it?

Related

.NET 7 System.IO.FileNotFoundException: Could not find file

This is my first time asking around here, so sorry if it's not worded very well.
I have a blazor WebAssembly project with MudBlazor, and when I try to upload files to save them into a database, it appears the next error message in the browser console.
System.IO.FileNotFoundException: Could not find file
When the user uploads the files I call the next method to save the files into IList<IBrowserFile>.
IList<IBrowserFile> Files = new List<IBrowserFile>();
private void OnInputFileChanged(InputFileChangeEventArgs e)
{
var files = e.GetMultipleFiles();
foreach(var file in files)
{
Files.Add(file);
}
}
Once the user has uploaded all the files, they click on a button that call the next method to upload it into a database.
[Inject] protected ISnackbar Snackbar { get; set; } = default!;
private async void Upload()
{
List<string>? notUploadFiles = null;
foreach(var file in Files)
{
byte[] fileBytes = File.ReadAllBytes(destPath + file.Name);
string extn = new FileInfo(file.Name).Extension;
var addArchivoTarea = new AddArchivoTareaRequestDTO(Tarea.Id, fileBytes, extn);
var successResponse = await HttpTareas.AddArchivoToTareaAsync(addArchivoTarea);
if (!successResponse)
{
notUploadFiles.Add(file.Name);
}
}
if(notUploadFiles is not null) {
Snackbar.Configuration.SnackbarVariant = Variant.Filled;
Snackbar.Add("The following files could not be uploaded:", Severity.Info);
Snackbar.Configuration.SnackbarVariant = Variant.Outlined;
foreach (var file in notUploadFiles)
{
Snackbar.Add(file, Severity.Error);
}
//Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
//Snackbar.Add("TODO: Upload your files!", Severity.Normal);
MudDialog.Close(DialogResult.Ok(true));
}
Snackbar.Add("All files have been successfully uploaded", Severity.Success);
MudDialog.Close(DialogResult.Ok(true));
}
I don't know where is the problem, any idea?
The uploaded files are not in the file system, they are in-memory only. The way to access the raw data would be something like:
byte[] fileBytes;
using (Stream s = file.OpenReadStream())
{
MemoryStream ms = new MemoryStream();
await s.CopyToAsync(ms);
fileBytes= ms.ToArray();
}

Unable to move a file once I have read and appended data to a new file c#

I have a foreach loop that is appending data from 1 file (source) to another (destination).
Once the file has been appended with all data I want to move the original source file. When debugging im getting the error "Exception thrown: 'System.IO.IOException' in System.IO.FileSystem.dll"
Im assuming this is because the file is locked. How can I dispose this so I can move the file.
var stringwrite = new[] { prefix, prefix2 };
foreach (string line in File.ReadLines(currentFile))
{
var lastOne = line.Split(';').Last();
if (!stringwrite.Any(stringwrite => lastOne.Contains(stringwrite)))
continue;
//write lines found to new file
File.AppendAllText(todestination, line + Environment.NewLine);
}
//move original file to new directory
try
{
File.Move(currentFile, completeddestination);
break;
}
catch (Exception ex)
{
MessageBox.Show("Issue Moving File");
}
As you are reading the source file to the end and writing to the destination depending on a condition, it would make sense to keep both input and output streams open until the end of read/write operations. Note, that File.ReadLines will open the source file, read the contents, and then close it. Also, File.AppendAllText will open the destination file, append the contents, and then close the file. Such an approach is inefficient. I think, your task could be properly implemented using file streams. Please find the example below:
static void Main()
{
var sourcePath = "C:\\PathToSource";
var destinationPath = "C:\\PathToDestination";
var completedDestinationPath = "C:\\PathToCompletedDestination";
var prefixes = new[] { "some-prefix", "some-other-prefix" };
foreach (var source in EnumerateDataFiles(sourcePath))
{
// This assumes that files in source and destination and completed
// dectination directories have the same file name but different paths.
// If you use another convention for your data, please adjust it here
var destination = GetDestinationFilePath(source, destinationPath);
var completedDestination = GetDestinationFilePath(source, completedDestinationPath);
try
{
AppendData(
source,
destination,
line =>
{
var lastEntry = line.Split(';').Last();
return prefixes.Any(prefix => lastEntry.Contains(prefix));
});
File.Move(source, completedDestination);
}
catch (Exception ex)
{
Console.WriteLine($"Issue Moving File: {ex.Message}");
}
}
}
static IEnumerable<string> EnumerateDataFiles(string path)
{
// Change *.dat to the extension (e.g., *.txt) you use for your data files,
// or to *.* to include all files from the directory
return Directory.EnumerateFiles(path, "*.dat", SearchOption.AllDirectories);
}
static string GetDestinationFilePath(string sourceFileName, string destinationPath)
{
// This will return a file path to the file with the same name as the source
// but located in the destination directory
return Path.Combine(destinationPath, Path.GetFileName(sourceFileName));
}
static void AppendData(string source, string destination, Func<string, bool> linePredicate)
{
using (var inputStream = new FileStream(source, FileMode.Open, FileAccess.Read))
using (var inputReader = new StreamReader(inputStream))
using (var outputStream = new FileStream(destination, FileMode.OpenOrCreate, FileAccess.Write))
using (var outputWriter = new StreamWriter(outputStream))
{
while (inputReader.ReadLine() is string inputLine)
{
if (!linePredicate(inputLine))
continue;
outputWriter.WriteLine(inputLine);
}
}
}
In the example provided, I assumed that you have the same file name but different paths for source, destination, and completed destination file paths. If you use a different naming mechanism, please follow comments to adjust it accordingly.

How to avoid exception The process cannot access the file because it is being used by another process. in .net

I am unzipping a.gz files by using the following code.Since i have huge number of files , I am using TPL tasks to run this code. But I used to get .NET exception:
The process cannot access the file because it is being used by another process.
How to correct this issue?
Code used is as follows:
private static void Decompress(FileInfo fileToDecompress)
{
using (FileStream originalFileStream = fileToDecompress.OpenRead())
{
string currentFileName = fileToDecompress.FullName;
string newFileName = currentFileName.Remove(currentFileName.Length -
fileToDecompress.Extension.Length);
using (FileStream decompressedFileStream = File.Create(newFileName))
{
using (GZipStream decompressionStream =
new GZipStream(originalFileStream,
CompressionMode.Decompress))
{
decompressionStream.CopyTo(decompressedFileStream);
}
}
}
//File.Move(myffile, Path.ChangeExtension(myffile, ".jpg"));
}
calling code is as shown below
var list = ftp.GetFileList(remotepath);
//-------------------
DateTime dt = DateTime.Now;
string st = String.Format("{0:yyyyMMdd}", dt);//20161120
Task[] myTasks = new Task[list.Count];
int i = 0;
foreach (string item in list)
{
if (item.StartsWith("GExport_") && (!item.ToUpper().Contains("DUM")) && (item.Contains(st)) && (!item.ToUpper().Contains("BLK")))
{
4gpath = item;
//Downloadfile()
ftp.Get(dtr["REMOTE_FILE_PATH"].ToString() + 4gpath , #localDestnDir + "\\" + dtr["SOURCE_PATH"].ToString());
download_location_hw = dtr["LOCAL_FILE_PATH"].ToString();
// Spin off a background task to process the file we just downloaded
myTasks[i++] = Task.Factory.StartNew(() =>
{
//Extractfile()
ExtractZipfiles(download_location_hw + "//" + huwawei4gpath, dtr["REMOTE_FILE_PATH"].ToString(),
dtr["FTP_SERVER"].ToString(), dtr["FTP_USER_ID"].ToString(),
dtr["TECH_CODE"].ToString(), dtr["VENDOR_CODE"].ToString());
}
}
}
private static void ExtractZipfiles(string download_location_hw,string remotepath,string server,string userid,string tech,string vendor)
{
if (download_location_hw.Contains(".gz"))
{
DirectoryInfo directorySelected = new DirectoryInfo(Path.GetDirectoryName(download_location_hw));
foreach (FileInfo fileToDecompress in directorySelected.GetFiles("*.gz"))
{
Decompress(fileToDecompress);
}
}
}
You're possibly extracting the zip files downloaded bu your tasks in the same folder:
download_location_hw = dtr["LOCAL_FILE_PATH"].ToString();
var directorySelected = new DirectoryInfo(Path.GetDirectoryName(download_location_hw));
If for somethat reason you have two files being targeted to the same local path, final foreach statement will have intersections with other tasks:
foreach (var fileToDecompress in directorySelected.GetFiles("*.gz"))
{
Decompress(fileToDecompress);
}
Here you're searching for all *.gz files in folder, even those ones that can be still in download mode or being decompressed by other process. So you should be definitely sure each tasks is being run in the different folder.
If you want to simple correction of your code, you may try this (the main change here is not to enumerate all the archive files but simply get one from the task arguments, with FileInfo constructor):
private static void ExtractZipfiles(string download_location_hw,string remotepath,string server,string userid,string tech,string vendor)
{
if (download_location_hw.Contains(".gz") && File.Exists(download_location_hw))
{
Decompress(new FileInfo(download_location_hw));
}
}
The various tasks are operating on the same download path and are causing a collission.
For eg. Task 1 puts a.gz, b.gz while Task 2 puts c.gz under "MyDir".
But now both tasks are operating and trying to rename the files to a, b, c during decompression. So, it appears you need to create subdirectories for each of the tasks or prefix\suffix the file names that are downloaded with unique identifiers.

Forcefully Replacing Existing Files during Extracting File using System.IO.Compression?

I am using the following code to extract all files in a folder
using (ZipArchive archive = new ZipArchive(zipStream))
{
archive.ExtractToDirectory(location);
}
But if one file exist then it throws an exception. Is there is any way to tell the Compression API to replace the existing files.
I found one way is to get all the file names first then check whether file exist and delete it. But this is somehow very costly for me.
I have created an extension. any comment to it improve will be appreciated,
public static class ZipArchiveExtensions
{
public static void ExtractToDirectory(this ZipArchive archive, string destinationDirectoryName, bool overwrite)
{
if (!overwrite)
{
archive.ExtractToDirectory(destinationDirectoryName);
return;
}
DirectoryInfo di = Directory.CreateDirectory(destinationDirectoryName);
string destinationDirectoryFullPath = di.FullName;
foreach (ZipArchiveEntry file in archive.Entries)
{
string completeFileName = Path.GetFullPath(Path.Combine(destinationDirectoryFullPath, file.FullName));
if (!completeFileName.StartsWith(destinationDirectoryFullPath, StringComparison.OrdinalIgnoreCase))
{
throw new IOException("Trying to extract file outside of destination directory. See this link for more info: https://snyk.io/research/zip-slip-vulnerability");
}
if (file.Name == "")
{// Assuming Empty for Directory
Directory.CreateDirectory(Path.GetDirectoryName(completeFileName));
continue;
}
file.ExtractToFile(completeFileName, true);
}
}
}
This code will not throw exception when the folder is not exist, instead of that it will create the folder.
public static class ZipArchiveExtensions
{
public static void ExtractToDirectory(this ZipArchive archive, string destinationDirectoryName, bool overwrite)
{
if (!overwrite)
{
archive.ExtractToDirectory(destinationDirectoryName);
return;
}
foreach (ZipArchiveEntry file in archive.Entries)
{
string completeFileName = Path.Combine(destinationDirectoryName, file.FullName);
string directory = Path.GetDirectoryName(completeFileName);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
if (file.Name != "")
file.ExtractToFile(completeFileName, true);
}
}
}
Take a look at this: Creating zip files easily in .NET 4.5. Your problem seems to be adressed. Alternatively, you can also check DotNetZip.
As I'm a total Linq fan, the Linq-ish way just for reference:
using (var strm = File.OpenRead(zipPath))
using (ZipArchive a = new ZipArchive(strm))
{
a.Entries.Where(o => o.Name == string.Empty && !Directory.Exists(Path.Combine(basePath, o.FullName))).ToList().ForEach(o => Directory.CreateDirectory(Path.Combine(basePath, o.FullName)));
a.Entries.Where(o => o.Name != string.Empty).ToList().ForEach(e => e.ExtractToFile(Path.Combine(basePath, e.FullName), true));
}
You can extract files to some temp directory and than copy files with the
"File.Copy" with the ovveride option true to your destination directory
I know that it's not a perfect solution , but this way you do not need to
check if file exist
Here is a method that takes a path to the zip file.
Based on the accepted answer.
public void ExtractZipFileToDirectory(string sourceZipFilePath, string destinationDirectoryName, bool overwrite)
{
using (var archive = ZipFile.Open(sourceZipFilePath, ZipArchiveMode.Read))
{
if (!overwrite)
{
archive.ExtractToDirectory(destinationDirectoryName);
return;
}
DirectoryInfo di = Directory.CreateDirectory(destinationDirectoryName);
string destinationDirectoryFullPath = di.FullName;
foreach (ZipArchiveEntry file in archive.Entries)
{
string completeFileName = Path.GetFullPath(Path.Combine(destinationDirectoryFullPath, file.FullName));
if (!completeFileName.StartsWith(destinationDirectoryFullPath, StringComparison.OrdinalIgnoreCase))
{
throw new IOException("Trying to extract file outside of destination directory. See this link for more info: https://snyk.io/research/zip-slip-vulnerability");
}
if (file.Name == "")
{// Assuming Empty for Directory
Directory.CreateDirectory(Path.GetDirectoryName(completeFileName));
continue;
}
file.ExtractToFile(completeFileName, true);
}
}
}
Since .NET Standard 2.1, it's as easy as setting overwriteFiles to true in:
ZipFile.ExtractToDirectory(string sourceFile, string destDir, Encoding entryNameEncoding, bool overwriteFiles)
Example:
ZipFile.ExtractToDirectory("c:\\file.zip","c:\\destination_folder", Encoding.UTF8, true);
Hi I'm using DotNetZip download from nugget.
I just simply use this code.
This will auto replace the files in the directory if exists.
"OverwriteSilently" !
using (ZipFile archive = new ZipFile(#"" + System.Environment.CurrentDirectory + "\\thezipfile.zip"))
{
archive.ExtractAll(#"" + System.Environment.CurrentDirectory, ExtractExistingFileAction.OverwriteSilently);
}
This is useful when you have zip file path
public static class ZipArchiveHelper
{
public static void ExtractToDirectory(string archiveFileName, string destinationDirectoryName, bool overwrite)
{
if (!overwrite)
{
ZipFile.ExtractToDirectory(archiveFileName, destinationDirectoryName);
}
else
{
using (var archive = ZipFile.OpenRead(archiveFileName))
{
foreach (var file in archive.Entries)
{
var completeFileName = Path.Combine(destinationDirectoryName, file.FullName);
var directory = Path.GetDirectoryName(completeFileName);
if (!Directory.Exists(directory) && !string.IsNullOrEmpty(directory))
Directory.CreateDirectory(directory);
if (file.Name != "")
file.ExtractToFile(completeFileName, true);
}
}
}
}
}
A little remade method from the answer to create all the folders
public static void ExtractToDirectory(this ZipArchive archive, string destinationDirectoryName, bool overwrite)
{
if (!overwrite)
{
archive.ExtractToDirectory(destinationDirectoryName);
return;
}
foreach (ZipArchiveEntry file in archive.Entries)
{
string completeFileName = Path.Combine(destinationDirectoryName, file.FullName);
if (file.Name == "")
{// Assuming Empty for Directory
Directory.CreateDirectory(Path.GetDirectoryName(completeFileName));
continue;
}
// create dirs
var dirToCreate = destinationDirectoryName;
for (var i = 0; i < file.FullName.Split('/').Length - 1; i++)
{
var s = file.FullName.Split('/')[i];
dirToCreate = Path.Combine(dirToCreate, s);
if (!Directory.Exists(dirToCreate))
Directory.CreateDirectory(dirToCreate);
}
file.ExtractToFile(completeFileName, true);
}
}
Looks like the only way to dodge including that blob of code is to simply delete the files before extracting the archive with del:
del (location + "\*")
using (ZipArchive archive = new ZipArchive(zipStream))
{
archive.ExtractToDirectory(location);
}
It's not exactly what the OP wanted, but it is compact.

C# unload image from imagebox

I have a method whit the following logic:
I delete all files from input and output folders
user selects an initial picture
this pictures as being assigned to imageBox1
picture is being copied to the folder input
magic happens to picture and I put it to the folder output
new picture from output is beign assigned to imageBox2
Here is where problem starts. When users wants to repeat operation imagebox1 and imagebox2 do have pictures assigned to them. And the step #0 failswith error
The process cannot access the file '03933.tiff' because it is being used by another process.
What I was trying is :
private void CopyImage_Click(object sender, EventArgs e)
{
string currentPath = Directory.GetCurrentDirectory();
string pathInput = currentPath + "\\Input";
string pathOutput = currentPath + "\\Output";
if (pictureBoxInput.Image != null) {
pictureBoxInput.Image = null;
pictureBoxInput.Dispose();
}
if (pictureBoxOutput.Image != null) {
pictureBoxOutput.Image = null;
pictureBoxOutput.Dispose();
}
System.IO.DirectoryInfo di = new DirectoryInfo(pathInput + "\\");
foreach (FileInfo file in di.GetFiles())
{
file.Delete();
}
System.IO.DirectoryInfo dia = new DirectoryInfo(pathOutput + "\\");
foreach (FileInfo file in dia.GetFiles())
{
file.Delete();
}
But still having the same error when program tries to delete files.
I would suggest changing:
if (pictureBoxInput.Image != null) {
pictureBoxInput.Image = null;
pictureBoxInput.Dispose();
}
to:
if (pictureBoxInput.Image != null) {
var existingFile = pictureBoxInput.Image;
pictureBoxInput.Image = null;
existingFile?.Dispose();
}
and the same for pictureBoxOutput
The issue is that your existing code is disposing the wrong thing - you are disposing the PictureBox rather than the Image (and the Image is the thing that is holding the file lock).

Categories