I have two network shares that both have dedicated accounts (network credentials) to access them
For example:
\RemoteComputer1\Folder - user1
\RemoteComputer2\Folder - user2
user1 don't have access to local computer and to \RemoteComputer2\Folder, same with user2, he doesn't have access to local computer and \RemoteComputer1\Folder.
I need to be able to do 3 types of operations:
Copy local file to \RemoteComputer1\Folder
Copy file from \RemoteComputer1\Folder to \RemoteComputer2\Folder
Copy file from \RemoteComputer1\Folder to local folder
Right now I'm using https://github.com/mj1856/SimpleImpersonation to get source stream and target stream and then I'm copying from source to target stream using stream.CopyTo
Below is my current code:
public static void CopyWithCredentials(string #sourcePath, string destinationPath, NetworkCredential readUser = null, NetworkCredential writeUser = null)
{
//same user, do normal File.Copy
if (readUser!=null && writeUser!=null && readUser == writeUser)
{
using (Impersonation.LogonUser(readUser.Domain, readUser.UserName, readUser.Password, LogonType.NewCredentials))
{
if (!Directory.Exists(Path.GetDirectoryName(destinationPath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
}
File.Copy(#sourcePath, destinationPath);
return;
}
}
FileStream sourceStream;
if (readUser != null)
{
using (Impersonation.LogonUser(readUser.Domain, readUser.UserName, readUser.Password, LogonType.NewCredentials))
{
sourceStream = new FileStream(#sourcePath, FileMode.OpenOrCreate, System.IO.FileAccess.Read);
}
}
else
{
sourceStream = new FileStream(#sourcePath, FileMode.OpenOrCreate, System.IO.FileAccess.Read);
}
FileStream destinationStream;
if (writeUser != null)
{
using (Impersonation.LogonUser(writeUser.Domain, writeUser.UserName, writeUser.Password, LogonType.NewCredentials))
{
if (!Directory.Exists(Path.GetDirectoryName(destinationPath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
}
while (File.Exists(destinationPath))
{
string fileName = Path.GetFileNameWithoutExtension(destinationPath);
string newFileName = fileName + "1";
destinationPath = destinationPath.Replace(fileName, newFileName);
}
destinationStream = File.Create(destinationPath);
}
}
else
{
if (!Directory.Exists(Path.GetDirectoryName(destinationPath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
}
destinationStream = File.Create(destinationPath);
}
#warning is this enough for closing streams after copying?
using (sourceStream)
{
using (destinationStream)
{
sourceStream.CopyTo(destinationStream);
}
}
sourceStream.Dispose();
destinationStream.Dispose();
sourceStream = null;
destinationStream = null;
}
I must admit this looks ugly and over-complicated.
Problem is I have multiple files in one folder and I want to copy all of them to second folder. Using my approach I call LogonUser twice for each file. For 1000 files I must call it 2000 times. Ideally I'd like to call LogonUser twice (ones for first folder and second time for second folder) and copy all files in that "session".
I would like to use File.Copy because it uses native kernel32.dll function (https://stackoverflow.com/a/1247092/965722) and as I found out in same question File.Copy has much improved since Vista SP1.
Also I found question about File.Copy and Stream speed and it looks like they should be equal, because File.Copy is using streams.
My question is: Can this be done simpler without using streams?
I don't want to create super admin account that has access everywhere.
I'd like to avoid WNetUseConnection because it can leave open connections.
I don't want to add permissions per files as mentioned in comments to this question.
Related
I am getting this error
" System.IO.IOException: The process cannot access the file 'C:\inetpub\wwwroot\wmo\Content\UploadFolder\Ad-2015 .xlsx' because it is being used by another process." when am executing my code. i developed my application using mvc and c# is using language.i put my published files in IIS wwwroot folder.
my code is
public ActionResult ImportData(HttpPostedFileBase file)
{if (file != null)
{
if (Request.Files["file"].ContentLength > 0)
{
string fileExtension = System.IO.Path.GetExtension(Request.Files["file"].FileName);
if (fileExtension == ".xls" || fileExtension == ".xlsx")
{
string fileLocation = Server.MapPath("~/Content/UploadFolder/") + Request.Files["file"].FileName;
loc = fileLocation;
if (System.IO.File.Exists(fileLocation))
{
System.IO.File.Delete(fileLocation);
}
Request.Files["file"].SaveAs(fileLocation); }}}}
I have getting the error from this line "System.IO.File.Delete(fileLocation);"
The web site likely has it open in a process that is not disposed. Check any code that references the file prior to this point that is IDisposable and wrap it in a using statement. If not disposed, it will hold the file open and you can't delete it.
Are you using FileStream instance without importing System.IO via a using statement?
here is my code and it's working as expected.
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
model.PhotoPath.CopyTo(fileStream);
}
After looking at many topics I decided to ask this
I have a WCF service that reads a file from the local file system. When the service is tested locally on my computer it was no problem doing that.
But when I publish the service in IIS8 i am getting this error
The system cannot find the file specified
I have tried creating a new user and new ApplicationPool that uses that identity to run the service and also given full control to the folder that is trying to be read but the problem continues.
I have also tried even using the Administrator as the identity of the new Application Pool but did not solve the problem either
What am i missing ?
Assuming that you have a relative URL and the account that is running the application has the proper permissions, you're probably not getting the correct pathname to your file.
You can try something like this to find the full path of your file:
using System.IO;
public FileInfo GetFileInfo(string filename)
{
if(filename == null)
throw new ArgumentNullException("filename");
FileInfo info = new FileInfo(filename);
if(!Path.IsPathRooted(filename) && !info.Exists)
{
string[] paths = {
Environment.CurrentDirectory,
AppDomain.CurrentDomain.BaseDirectory,
HostingEnvironment.ApplicationPhysicalPath,
};
foreach(var path in paths)
{
if(path != null)
{
string file = null;
file = Path.Combine(path, filename);
if(File.Exists(file))
{
return new FileInfo(file);
}
}
}
}
throw new FileNotFoundException("Couldn not find the requested file", filename);
}
It's returning an instance of System.IO.FileInfo but you can easily adapt it to return a string (full pathname).
I'm trying to open a stream to a file.
First I need to save a file to my desktop and then open a stream to that file.
This code works well (from my previous project) but in this case, I don't want to prompt the user to pick the save location or even the name of the file. Just save it and open the stream:
Stream myStream;
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
if ((myStream = saveFileDialog1.OpenFile()) != null)
{
PdfWriter.GetInstance(document, myStream);
Here's my code for the newer project (the reason for this question):
namespace Tutomentor.Reporting
{
public class StudentList
{
public void PrintStudentList(int gradeParaleloID)
{
StudentRepository repo = new StudentRepository();
var students = repo.FindAllStudents()
.Where(s => s.IDGradeParalelo == gradeParaleloID);
Document document = new Document(PageSize.LETTER);
Stream stream;
PdfWriter.GetInstance(document, stream);
document.Open();
foreach (var student in students)
{
Paragraph p = new Paragraph();
p.Content = student.Name;
document.Add(p);
}
}
}
}
Use Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) to get the desktop directory.
string fileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),
"MyFile.pdf");
using(var stream = File.OpenWrite(fileName))
{
PdfWriter.GetInstance(document, stream);
}
// However you initialize your instance of StudentList
StudentList myStudentList = ...;
using (FileStream stream = File.OpenWrite(#"C:\Users\me\Desktop\myDoc.pdf")) {
try {
myStudentList.PrintStudentList(stream, gradeParaleloID);
}
finally {
stream.Close();
}
}
You should pass the stream into your method:
public void PrintStudentList(Stream stream, int gradeParaleloID) { ... }
EDIT
Even though I hard coded a path above, you shouldn't do that, use something like this to get the path to your desktop:
Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
If this is a local (e.g. Windows/console) application just make the stream a FileStream to whatever path you want (check this for info on how to get the desktop folder path). If the user running the application has write permitions to that file it will be created/saved there.
If this is a web (e.g. ASP.Net) application you won't be able to save the file directly in the client machine without prompting the user (for security reasons).
Stream myStream = new FileStream(#"c:\Users\[user]\Desktop\myfile.dat", FileMode.OpenOrCreate);
Your FileMode may differ depending on what you're trying to do. Also I wouldn't advise actually using the Desktop for this, but that's what you asked for in the question. Preferably, look into Isolated Storage.
I'm using IsolatedStorage in a Silverlight application for caching, so I need to know if the file exists or not which I do with the following method.
I couldn't find a FileExists method for IsolatedStorage so I'm just catching the exception, but it seems to be a quite general exception, I'm concerned it will catch more than if the file doesn't exist.
Is there a better way to find out if a file exists in IsolatedStorage than this:
public static string LoadTextFromIsolatedStorageFile(string fileName)
{
string text = String.Empty;
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
try
{
using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fileName,
FileMode.Open, isf))
{
using (StreamReader sr = new StreamReader(isfs))
{
string lineOfData = String.Empty;
while ((lineOfData = sr.ReadLine()) != null)
text += lineOfData;
}
}
return text;
}
catch (IsolatedStorageException ex)
{
return "";
}
}
}
From the "manual" (.net framework 2.0 Application Development Foundation):
Unlike the application programming interface (API) for files stored arbitrarily
in the file system, the API for files in Isolated Storage does not support checking
for the existence of a file directly like File.Exists does. Instead, you need to ask the
store for a list of files that match a particular file mask. If it is found, you can open the
file, as shown in this example
string[] files = userStore.GetFileNames("UserSettings.set");
if (files.Length == 0)
{
Console.WriteLine("File not found");
}
else
{
// ...
}
The following code gives me a System.IO.IOException with the message 'The process cannot access the file'.
private void UnPackLegacyStats()
{
DirectoryInfo oDirectory;
XmlDocument oStatsXml;
//Get the directory
oDirectory = new DirectoryInfo(msLegacyStatZipsPath);
//Check if the directory exists
if (oDirectory.Exists)
{
//Loop files
foreach (FileInfo oFile in oDirectory.GetFiles())
{
//Check if file is a zip file
if (C1ZipFile.IsZipFile(oFile.FullName))
{
//Open the zip file
using (C1ZipFile oZipFile = new C1ZipFile(oFile.FullName, false))
{
//Check if the zip contains the stats
if (oZipFile.Entries.Contains("Stats.xml"))
{
//Get the stats as a stream
using (Stream oStatsStream = oZipFile.Entries["Stats.xml"].OpenReader())
{
//Load the stats as xml
oStatsXml = new XmlDocument();
oStatsXml.Load(oStatsStream);
//Close the stream
oStatsStream.Close();
}
//Loop hit elements
foreach (XmlElement oHitElement in oStatsXml.SelectNodes("/*/hits"))
{
//Do stuff
}
}
//Close the file
oZipFile.Close();
}
}
//Delete the file
oFile.Delete();
}
}
}
I am struggling to see where the file could still be locked. All objects that could be holding onto a handle to the file are in using blocks and are explicitly closed.
Is it something to do with using FileInfo objects rather than the strings returned by the static GetFiles method?
Any ideas?
I do not see problems in your code, everything look ok. To check is the problem lies in C1ZipFile I suggest you initialize zip from stream, instead of initialization from file, so you close stream explicitly:
//Open the zip file
using (Stream ZipStream = oFile.OpenRead())
using (C1ZipFile oZipFile = new C1ZipFile(ZipStream, false))
{
// ...
Several other suggestions:
You do not need to call Close() method, with using (...), remove them.
Move xml processing (Loop hit elements) outsize zip processing, i.e. after zip file closeing, so you keep file opened as least as possible.
I assume you're getting the error on the oFile.Delete call. I was able to reproduce this error. Interestingly, the error only occurs when the file is not a zip file. Is this the behavior you are seeing?
It appears that the C1ZipFile.IsZipFile call is not releasing the file when it's not a zip file. I was able to avoid this problem by using a FileStream instead of passing the file path as a string (the IsZipFile function accepts either).
So the following modification to your code seems to work:
if (oDirectory.Exists)
{
//Loop files
foreach (FileInfo oFile in oDirectory.GetFiles())
{
using (FileStream oStream = new FileStream(oFile.FullName, FileMode.Open))
{
//Check if file is a zip file
if (C1ZipFile.IsZipFile(oStream))
{
// ...
}
}
//Delete the file
oFile.Delete();
}
}
In response to the original question in the subject: I don't know if it's possible to know if a file can be deleted without attempting to delete it. You could always write a function that attempts to delete the file and catches the error if it can't and then returns a boolean indicating whether the delete was successful.
I'm just guessing: are you sure that oZipFile.Close() is enough? Perhaps you have to call oZipFile.Dispose() or oZipFile.Finalize() to be sure it has actually released the resources.
More then Likely it's not being disposed, anytime you access something outside of managed code(streams, files, etc.) you MUST dispose of them. I learned the hard way with Asp.NET and Image files, it will fill up your memory, crash your server, etc.
In the interest of completeness I am posing my working code as the changes came from more than one source.
private void UnPackLegacyStats()
{
DirectoryInfo oDirectory;
XmlDocument oStatsXml;
//Get the directory
oDirectory = new DirectoryInfo(msLegacyStatZipsPath);
//Check if the directory exists
if (oDirectory.Exists)
{
//Loop files
foreach (FileInfo oFile in oDirectory.GetFiles())
{
//Set empty xml
oStatsXml = null;
//Load file into a stream
using (Stream oFileStream = oFile.OpenRead())
{
//Check if file is a zip file
if (C1ZipFile.IsZipFile(oFileStream))
{
//Open the zip file
using (C1ZipFile oZipFile = new C1ZipFile(oFileStream, false))
{
//Check if the zip contains the stats
if (oZipFile.Entries.Contains("Stats.xml"))
{
//Get the stats as a stream
using (Stream oStatsStream = oZipFile.Entries["Stats.xml"].OpenReader())
{
//Load the stats as xml
oStatsXml = new XmlDocument();
oStatsXml.Load(oStatsStream);
}
}
}
}
}
//Check if we have stats
if (oStatsXml != null)
{
//Process XML here
}
//Delete the file
oFile.Delete();
}
}
}
The main lesson I learned from this is to manage file access in one place in the calling code rather than letting other components manage their own file access. This is most apropriate when you want to use the file again after the other component has finished it's task.
Although this takes a little more code you can clearly see where the stream is disposed (at the end of the using), compared to having to trust that a component has correctly disposed of the stream.