Web服务中文件下载的多线程并发问题

发布于 2024-11-04 14:21:30 字数 2521 浏览 0 评论 0原文

我有一个 asmx Web 服务,用于将文件下载到客户端。我的下载方法如下所示:

[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;
            }
        }

我的问题是,当我强调 100 个并发用户下载时,我得到一个异常:(

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

文件名模糊)

任何人都知道如何解决这个并发问题,是我的锁不正确吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浅忆 2024-11-11 14:21:31

快速而肮脏地,使用静态私有对象作为锁:

private static object padlock = new object();

public byte[] DownloadFile(int id) {
  lock(padlock) {
      // code here
  }
}

您当前的锁仅存在于所创建的类的实例上。为每个请求创建该类的新实例。我的解决方案存在以下问题:所有文件都将按顺序读取。如果这会导致性能问题,我只会考虑不同的方法。如果您想要并发访问不同的文件,您最终需要锁定文件名。这可能需要更多的工作。

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.

一曲爱恨情仇 2024-11-11 14:21:31

我猜想在使用 FileStream 之后处理它会有所帮助。

尝试将其包含在 using 块中,如下所示:

using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read)) 
{
   ....
}

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)) 
{
   ....
}
清旖 2024-11-11 14:21:31

以下内容将简化您的代码,并且如果问题是由于未处理 FileStream 引起的,则可能会解决该问题:

string path = Server.MapPath(RepositoryDir + "/" + file.DiscName);
byte[] content = File.ReadAllBytes(path);

File.ReadAllBytes 使用 FileAccess.Read 打开文件FileShare.Read

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文