Sun*_*nil 4 c# asp.net file-io
我在ASP.Net应用程序的代码隐藏中有以下代码,其中正在读取文件,然后写入文件.
码
var state= File.ReadAllText(Server.MapPath(string.Format("~/state/{0}", fileName)));
if(state.indexOf("1") == 0)
{
File.WriteAllText(Server.MapPath(string.Format("~/state/{0}", fileName)), newState);
}
Run Code Online (Sandbox Code Playgroud)
有时候,但并非总是如此,我得到以下异常.
例外
The process cannot access the file 'C:\inetpub\wwwroot\mywebsite1\state\20150905005929435_edf9267e-fad1-45a7-bfe2-0e6e643798b5' because it is being used by another process.
我猜测文件读取操作有时不会在写入操作发生之前关闭文件,或者可能是文件写入操作未在Web应用程序发出下一个请求之前关闭文件.但是,我找不到究竟是什么原因.
问题:如何避免发生此错误?使用File类是不安全的,而是使用FileStream对象的传统方法,我总是显式地处理FileStream对象?
更新1
我尝试了一种重试循环方法,但即使这似乎也没有解决问题,因为如果ASP.Net页面一次又一次非常快速地提交,我能够重现相同的错误.所以我回到了我的案例中找到一个万无一失的解决方案.
string state = null;
int i = 0;
while (i < 20) {
try {
state = File.ReadAllText(Server.MapPath(string.Format("~/state/{0}", fileName)));
} catch (Exception ex2) {
//log exception
Elmah.ErrorSignal.FromCurrentContext().Raise(ex2);
//if even retry doesn't work then throw an exception
if (i == 19) {
throw;
}
//sleep for a few milliseconds
System.Threading.Thread.Sleep(10);
}
i++;
}
i = 0;
while (i < 20) {
try {
File.WriteAllText(Server.MapPath(string.Format("~/state/{0}", fileName)), newState);
} catch (Exception ex2) {
//log exception
Elmah.ErrorSignal.FromCurrentContext().Raise(ex2);
//if even retry doesn't work then throw an exception
if (i == 19) {
throw;
}
//sleep for a few milliseconds
System.Threading.Thread.Sleep(10);
}
i++;
}
Run Code Online (Sandbox Code Playgroud)
更新2
似乎唯一有效的傻瓜式解决方案是使用文件排序方法,如下所述usr.这涉及写入不同的文件而不是刚刚读取的同一文件.要写入的文件的名称是刚刚读取的文件的名称,附加了序列号.
string fileName = hiddenField1.Value;
string state = null;
int i = 0;
while (i < 20) {
try {
state = File.ReadAllText(Server.MapPath(string.Format("~/state/{0}", fileName)));
} catch (Exception ex2) {
//log exception
Elmah.ErrorSignal.FromCurrentContext().Raise(ex2);
//if even retry doesn't work then throw an exception
if (i == 19) {
throw;
}
//sleep for a few milliseconds
System.Threading.Thread.Sleep(10);
}
i++;
}
i = 0;
while (i < 20) {
try {
//***************FILE SEQUENCING**************************
//Change the file to which state is written, so no concurrency errors happen
//between reading from and writing to same file. This is a fool-proof solution.
//Since max value of integer is more than 2 billion i.e. 2,147,483,647
//so we can be sure that our sequence will never run out of limits because an ASP.Net page
//is not going to postback 2 billion times
if (fileName.LastIndexOf("-seq_") >= 0) {
fileName = fileName.Substring(0, fileName.LastIndexOf("-seq_") + 4 + 1) + (int.Parse(fileName.Substring(fileName.LastIndexOf("-seq_") + 4 + 1)) + 1);
} else {
fileName = fileName + "-seq_1";
}
//change the file name so in next read operation the new file is read
hiddenField1.Value = fileName;
File.WriteAllText(Server.MapPath(string.Format("~/state/{0}", fileName)), newState);
} catch (Exception ex2) {
//log exception
Elmah.ErrorSignal.FromCurrentContext().Raise(ex2);
//if even retry doesn't work then throw an exception
if (i == 19) {
throw;
}
//sleep for a few milliseconds
System.Threading.Thread.Sleep(10);
}
i++;
}
Run Code Online (Sandbox Code Playgroud)
上述方法的唯一缺点是,当最终用户回发到同一个ASP.Net页面时,会创建许多文件.因此,最好有一个删除陈旧文件的后台作业,这样可以最大限度地减少文件数量.
文件名与排序
更新3
另一个万无一失的解决方案是在读写文件名之间进行切换.这样我们最终不会创建多个文件,只使用2个文件,因为最终用户多次回发到同一页面.代码与UPDATE 2下的代码相同,但FILE SEQUENCING注释后的代码应替换为下面的代码.
if (fileName.LastIndexOf("-seq_1") >= 0) {
fileName = fileName.Substring(0, fileName.LastIndexOf("-seq_1"));
} else {
fileName = fileName + "-seq_1";
}
Run Code Online (Sandbox Code Playgroud)
我猜测文件读取操作有时不会在写入操作发生之前关闭文件,或者可能是文件写入操作未在Web应用程序发出下一个请求之前关闭文件.
正确.文件系统不支持原子更新.(尤其不是在Windows上;很多怪癖.)
使用FileStream没有帮助.您只需重写File该类具有的相同代码.File里面没有魔法.它只是FileStream为了您的方便使用包装.
尝试保持文件不可变.当你想写一个新内容时写一个新文件.在文件名中附加序列号(例如ToString("D9")).读取时选择序列号最高的文件.
或者,只需添加一个延迟较小的重试循环.
或者,使用更好的数据存储,例如数据库.文件系统真的很讨厌.例如,使用SQL Server解决这个问题很容易.
| 归档时间: |
|
| 查看次数: |
1139 次 |
| 最近记录: |