尝试上载文件时出现ObjectDisposedException

时间:2013-12-02 15:20:37

标签: c# asp.net-mvc file-upload

我有这个服务类:

public delegate string AsyncMethodCaller(string id, HttpPostedFileBase file);

public class ObjectService : IDisposable
{
    private readonly IObjectRepository repository;
    private readonly IAmazonS3 client;
    private readonly string bucketName;

    private static object syncRoot = new object();
    private static IDictionary<string, int> processStatus { get; set; }

    public ObjectService(string accessKey, string secretKey, string bucketName)
    {
        var credentials = new BasicAWSCredentials(accessKey, secretKey);

        this.bucketName = bucketName;
        this.client = new AmazonS3Client(credentials, RegionEndpoint.EUWest1);
        this.repository = new ObjectRepository(this.client, this.bucketName);

        if (processStatus == null)
            processStatus = new Dictionary<string, int>();
    }

    public IList<S3Object> GetAll()
    {
        return this.repository.GetAll();
    }

    public S3Object Get(string key)
    {
        return this.GetAll().Where(model => model.Key.Equals(key, StringComparison.OrdinalIgnoreCase)).SingleOrDefault();
    }

    /// <summary>
    /// Note: You can upload objects of up to 5 GB in size in a single operation. For objects greater than 5 GB you must use the multipart upload API.
    /// Using the multipart upload API you can upload objects up to 5 TB each. For more information, see http://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html.
    /// </summary>
    /// <param name="id">Unique id for tracking the upload progress</param>
    /// <param name="bucketName">The name of the bucket that the object is being uploaded to</param>
    /// <param name="file">The file that will be uploaded</param>
    /// <returns>The unique id</returns>
    public string Upload(string id, HttpPostedFileBase file)
    {
        var reader = new BinaryReader(file.InputStream);
        var data = reader.ReadBytes((int)file.InputStream.Length);
        var stream = new MemoryStream(data);
        var utility = new TransferUtility(client);

        var request = new TransferUtilityUploadRequest()
        {
            BucketName = this.bucketName,
            Key = file.FileName,
            InputStream = stream
        };

        request.UploadProgressEvent += (sender, e) => request_UploadProgressEvent(sender, e, id);
        utility.Upload(request);

        return id;
    }

    private void request_UploadProgressEvent(object sender, UploadProgressArgs e, string id)
    {
        lock (syncRoot)
        {
            processStatus[id] = e.PercentDone;
        }
    }

    public void Add(string id)
    {
        lock (syncRoot)
        {
            processStatus.Add(id, 0);
        }
    }

    public void Remove(string id)
    {
        lock (syncRoot)
        {
            processStatus.Remove(id);
        }
    }

    public int GetStatus(string id)
    {
        lock (syncRoot)
        {
            if (processStatus.Keys.Count(x => x == id) == 1)
            {
                return processStatus[id];
            }
            else
            {
                return 100;
            }
        }
    }

    public void Dispose()
    {
        this.repository.Dispose();
        this.client.Dispose();
    }
}

我的控制器看起来像这样:

public class _UploadController : Controller
{
    public void StartUpload(string id, HttpPostedFileBase file)
    {
        var bucketName = CompanyProvider.CurrentCompanyId();

        using (var service = new ObjectService(ConfigurationManager.AppSettings["AWSAccessKey"], ConfigurationManager.AppSettings["AWSSecretKey"], bucketName))
        {
            service.Add(id);

            var caller = new AsyncMethodCaller(service.Upload);
            var result = caller.BeginInvoke(id, file, new AsyncCallback(CompleteUpload), caller);
        }  
    }

    public void CompleteUpload(IAsyncResult result)
    {
        var caller = (AsyncMethodCaller)result.AsyncState;
        var id = caller.EndInvoke(result);
    }

    //
    // GET: /_Upload/GetCurrentProgress

    public JsonResult GetCurrentProgress(string id)
    {
        try
        {
            var bucketName = CompanyProvider.CurrentCompanyId();
            this.ControllerContext.HttpContext.Response.AddHeader("cache-control", "no-cache");

            using (var service = new ObjectService(ConfigurationManager.AppSettings["AWSAccessKey"], ConfigurationManager.AppSettings["AWSSecretKey"], bucketName))
            {
                return new JsonResult { Data = new { success = true, progress = service.GetStatus(id) } };
            }
        }
        catch (Exception ex)
        {
            return new JsonResult { Data = new { success = false, error = ex.Message } };
        }
    }
}

现在,我发现有时,当我尝试在此行上传文件时,我收到错误 ObjectDisposedException var data = reader.ReadBytes((int)file.InputStream。长度); 即可。我读到我不应该使用使用关键字,因为异步调用但它似乎仍然在处理流。

谁能告诉我为什么?

更新1

我已将控制器更改为:

private ObjectService service = new ObjectService(ConfigurationManager.AppSettings["AWSAccessKey"], ConfigurationManager.AppSettings["AWSSecretKey"], CompanyProvider.CurrentCompanyId());

public void StartUpload(string id, HttpPostedFileBase file)
{            
    service.Add(id);

    var caller = new AsyncMethodCaller(service.Upload);
    var result = caller.BeginInvoke(id, file, new AsyncCallback(CompleteUpload), caller);
}

public void CompleteUpload(IAsyncResult result)
{
    var caller = (AsyncMethodCaller)result.AsyncState;
    var id = caller.EndInvoke(result);

    this.service.Dispose();
}

但我仍然在file.InputStream行上收到错误。

更新2

问题似乎在于BinaryReader。 我将代码更改为:

        var inputStream = file.InputStream;
        var i = inputStream.Length;
        var n = (int)i;

        using (var reader = new BinaryReader(inputStream))
        {
            var data = reader.ReadBytes(n);
            var stream = new MemoryStream(data);

            var request = new TransferUtilityUploadRequest()
            {
                BucketName = this.bucketName,
                Key = file.FileName,
                InputStream = stream
            };

            try
            {
                request.UploadProgressEvent += (sender, e) => request_UploadProgressEvent(sender, e, id);
                utility.Upload(request);
            }
            catch
            {
                file.InputStream.Dispose(); // Close our stream
            }
        }

如果上传失败并且我尝试重新上传该项目,那就是抛出错误的时间。这就像物品被锁定或其他东西。

1 个答案:

答案 0 :(得分:0)

使用BeginInvoke调用服务时,使用using语句处理服务。

using (var service = new ObjectService(ConfigurationManager.AppSettings["AWSAccessKey"], ConfigurationManager.AppSettings["AWSSecretKey"], bucketName))
        {
            service.Add(id);

            var caller = new AsyncMethodCaller(service.Upload);
            var result = caller.BeginInvoke(id, file, new AsyncCallback(CompleteUpload), caller);
        }  

完成工作后,您必须处理您的服务:

var service = new ObjectService(ConfigurationManager.AppSettings["AWSAccessKey"], ConfigurationManager.AppSettings["AWSSecretKey"], bucketName)

public void StartUpload(string id, HttpPostedFileBase file)
    {
        var bucketName = CompanyProvider.CurrentCompanyId();


            service.Add(id);

            var caller = new AsyncMethodCaller(service.Upload);
            var result = caller.BeginInvoke(id, file, new AsyncCallback(CompleteUpload), caller);

    }

    public void CompleteUpload(IAsyncResult result)
    {

    var caller = (AsyncMethodCaller)result.AsyncState;
    var id = caller.EndInvoke(result);
    service.Close();
        service.Dispose();
    }

此外,您的文件可能已损坏,请尝试以下代码:

byte[] buffer = new byte[file.InputStream.Length];
file.InputStream.Seek(0, SeekOrigin.Begin);
file.InputStream.Read(buffer, 0, file.InputStream.Length);