我们通过将新logging写入临时文件来执行大文本文件的更新,然后用临时文件replace旧文件。 一个严重的缩写版本:
var tpath = Path.GetTempFileName(); try { using (var sf = new StreamReader(sourcepath)) using (var tf = new StreamWriter(tpath)) { string line; while ((line = sf.ReadLine()) != null) tf.WriteLine(UpdateLine(line)); } File.Delete(sourcepath); File.Move(tpath, sourcepath); } catch { File.Delete(tpath); throw; }
如果有什么东西抛出exception(文件没有find,没有权限),原始文件保持不变,这是我们想要的。
但是,代码有以下问题:
是否存在Delete
工作但Move
失败的真实情况? 这将删除原始和更新的数据。 这会很糟糕。
最常见的故障是从另一个应用程序打开的源文件,并且Delete
失败。 这意味着所有的更新工作都被丢弃。 有没有办法查看源文件在开始时是否可删除,如果不是,则放弃更新?
我们有用户将Windows资源pipe理器摘要属性(如标题或注释)放在文件上。 当我们删除文件时,这些被放弃。 有没有办法将旧文件的摘要属性复制到一个新的文件? 我们应该这样做吗?
避免“删除然后移动失败问题”的正常方法是:
然后当你来读,如果file.current缺少使用file.new,如果你看到它删除file.old。
检查文件是否可用:尝试打开它进行写入,但追加到结尾。 当然,在你移动之前,你需要关闭句柄,而在别人之间可以打开它 – 但至少是一个合理的优化。
不知道复制摘要等,恐怕。
为什么不先尝试检查FileAttributes?
尝试这样的事情:
//If File is readonly if ( (file.Attribute & System.FileAttributes.ReadOnly) == System.FileAttributes.ReadOnly ) //Don't delete.
另外尝试使用.OpenWrite()。 如果您可以打开要写入的文件,则不会被访问,也不会被使用。 如果文件当前处于未打开状态,则只能打开文件进行写入。 我不推荐这个,但它可以帮助你。
FileStream fs = File.OpenWrite(file); fs.Close(); return false;
您也可以使用FileLock检查方法。 像这样的东西:
protected virtual bool IsFileLocked(FileInfo file) { try { using (file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { return false; } } catch (IOException) { return true; }
}
你可能也想检查FileIOPermission.Write。 这允许查看文件是否可写(并且能够删除)。
fileIOPerm = New FileIOPermission(FileIOPermissionAccess.Write, FileSpec); fileIOPerm.Demand();
关于原始帖子中的#3问题…您始终可以使用File.Copy(path1,path2,true)将文件移动到临时文件夹。 您可能需要考虑使用临时文件夹并为文件操作编写更好的逻辑。
如果你决定使用临时文件夹或临时文件/中间文件,那么你也可以修复你的问题#2。 尝试先移动文件。
一些“脏”的把戏。
首先不要删除原来的文件,先把它移动到另一个位置(temp path),然后如果更新文件的移动成功,则删除旧的文件。 如果更新失败,您可以将原始文件恢复到某个位置。
我认为这篇文章将帮助你在那里MSDN
如果用户需要“标题”和“评论”,你应该保留它们。 我从来没有试图从一个文件复制到另一个,所以我不知道如何帮助你。
Windows Vista或更高版本上的事务性NTFS可能对您的方案有用。
正如已经提到的,你真的应该调查ReplaceFile,它旨在帮助你正在做什么。 .NET函数只是Win32函数的一个包装器,在这个函数中可能会有一些希望,即原子性问题已经被敲定。
很多好的建议。 我能够解决以下问题:
var sInfo = new FileInfo(sourcePath); if (sInfo.IsReadOnly) throw new IOException("File '" + sInfo.FullName + "' is read-only."); var tPath = Path.GetTempFileName(); try { // This throws if sourcePath does not exist, is opened, or is not readable. using (var sf = sInfo.OpenText()) using (var tf = new StreamWriter(tPath)) { string line; while ((line = sf.ReadLine()) != null) tf.WriteLine(UpdateLine(line)); } string backupPath = sInfo.FullName + ".bak"; if (File.Exists(backupPath)) File.Delete(backupPath); File.Move(tPath, backupPath); tPath = backupPath; File.Replace(tPath, sInfo.FullName, null); } catch (Exception ex) { File.Delete(tPath); throw new IOException("File '" + sInfo.FullName + "' could not be overwritten.", ex); }
如果源文件打开或不可读,则OpenText
会抛出,并且更新没有完成。 如果有任何东西抛出,原始文件保持不变。 Replace
旧文件的摘要属性Replace
为新文件。 即使源文件与临时文件夹位于不同的卷上,也是如此。
我发现把这个模式包装在自己的类中是很有用的。
class Program { static void Main( string[] args ) { using( var ft = new FileTransaction( @"C:\MyDir\MyFile.txt" ) ) using( var sw = new StreamWriter( ft.TempPath ) ) { sw.WriteLine( "Hello" ); ft.Commit(); } } } public class FileTransaction :IDisposable { public string TempPath { get; private set; } private readonly string filePath; public FileTransaction( string filePath ) { this.filePath = filePath; this.TempPath = Path.GetTempFileName(); } public void Dispose() { if( TempPath != null ) { try { File.Delete( TempPath ); } catch { } } } public void Commit() { try { var oldPath = filePath + ".old"; File.Move( filePath, oldPath ); } catch {} File.Move( TempPath, filePath ); TempPath = null; } }
这个代码片断展示了一种获取文件独占访问的技巧(在这种情况下阅读):
// Try to open a file exclusively FileInfo fi = new FileInfo(fullFilePath); int attempts = maxAttempts; do { try { // Try to open for reading with exclusive access... fs = fi.Open(FileMode.Open, FileAccess.Read, FileShare.None); } // Ignore any errors... catch { } if (fs != null) { break; } else { Thread.Sleep(100); } } while (--attempts > 0); // Did we manage to open file exclusively? if (fs != null) { // use open file.... }