Reputation: 4209
I have a asmx web service that is used to download files to clients. My method for downloading is shown below:
[WebMethod]
public byte[] DownloadFile(int id)
{
lock (this)
{
if (id == 0)
{
throw new ArgumentException("Input value is not valid");
}
IGWFileRepository fileRepository = new GWFileRepository();
GWFile file = fileRepository.GetFileById(id);
string path = Server.MapPath(RepositoryDir + "/" + file.DiscName);
BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read));
reader.BaseStream.Position = 0;
byte[] content = reader.ReadBytes(Convert.ToInt32(reader.BaseStream.Length));
reader.Close();
return content;
}
}
My problem is that when i stress it with 100 simultaneous users downloading i get an exception:
Exception: System.Web.Services.Protocols.SoapException: System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> System.IO.IOException: The process cannot access the file 'C:\Subversion\Repository\cb0a27e2-2d23-43b1-a12e-f07fb401cfc9.jpg' because it is being used by another process.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
at System.IO.File.Open(String path, FileMode mode, FileAccess access)
at GrenWebRepository.RepositoryService.DownloadFile(Int32 id) in C:\Subversion\Repository\RepositoryService.asmx.cs:line 62
--- End of inner exception stack trace ---
at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
at RepositoryTester.RS.RepositoryService.DownloadFile(Int32 id) in C:\Subversion\Repository\Web References\RS\Reference.cs:line 174
at RepositoryTester.User.DownloadAction() in C:\Subversion\Repository\RepositoryTester\RepositoryTester\User.cs:line 110
(file names obscured)
Any one know how to solve this concurrency problem, is my lock not proper?
Upvotes: 2
Views: 1740
Reputation: 5132
Quick and dirty, use a static private object to use as a lock:
private static object padlock = new object();
public byte[] DownloadFile(int id) {
lock(padlock) {
// code here
}
}
Your current lock only exists on the instance of the class that was created. New instances of the class are created for every request. My solution has the issue that all files will be read sequentially. I would only look at a different method if this causes performance problems. You will ultimately need to lock on the file name, if you want concurrent access to different files. That may take a bit more work.
Upvotes: 1
Reputation: 133975
The following will simplify your code, and might solve the problem if it was caused by not disposing of the FileStream
:
string path = Server.MapPath(RepositoryDir + "/" + file.DiscName);
byte[] content = File.ReadAllBytes(path);
File.ReadAllBytes
opens the file with FileAccess.Read
and FileShare.Read
.
Upvotes: 0
Reputation: 1522
I would guess disposing the FileStream after using it would help.
Try including it in a using block like so:
using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read))
{
....
}
Upvotes: 0