IOException on streamed file upload via WCF over HTTP

时光总嘲笑我的痴心妄想 提交于 2019-12-25 07:28:13

问题


I'm building a WCF service hosted that allows the client to upload files over HTTP. The service reads the client's Stream chunk by chunk. This works well for a small file, where only a single iteration is needed. But when uploading larger files after some chunks I get an IOException saying An exception has been thrown when reading the stream. on Stream.EndRead().

The inner exception is The I/O operation has been aborted because of either a thread exit or an application request.

The amount of read chunks varies but I can't really figure out, what causes the difference. The time it works varies from 300ms to 550 ms and ~1MB up to ~2MB processed.

Does anyone have a clue?

The interface is defined like this:

[ServiceContract]
  public interface IServiceFileserver
  {
    [OperationContract]
    UploadResponse UploadFile(UploadRequest uploadRequest);

    // All status feedback related code is left out for simplicity
    // [OperationContract]
    // RunningTaskStatus GetProgress(Guid taskId); 
  }

  [MessageContract]
  public class UploadRequest
  {
    [MessageHeader()]
    public string FileName { get; set; }

    [MessageHeader()]
    public long SizeInByte { get; set; }

    [MessageBodyMember(Order = 1)]
    public Stream Stream { get; set; }
  }

  [MessageContract]
  public class UploadResponse
  {
    [MessageBodyMember()]
    public Guid TaskId { get; set; }
  }

Here is the service implementation:

const int bufferSize = 4 * 1024;
// This is called from the client side
public UploadResponse UploadFile(UploadRequest uploadRequest)
{
  Guid taskId = Guid.NewGuid();
  Stream stream = null;
  try
  {
    stream = uploadRequest.Stream;
    string filename = uploadRequest.FileName;
    long sizeInBytes = uploadRequest.SizeInByte;
    byte[] buffer = new byte[bufferSize];

    stream.BeginRead(buffer, 0, bufferSize, ReadAsyncCallback, new AsyncHelper(buffer, stream, sizeInBytes));
  }
  catch (Exception ex)
  {
    if (stream != null)
      stream.Close();
  }
  return new UploadResponse() { TaskId = taskId };
}

// Helper class for the async reading
public class AsyncHelper
{
  public Byte[] ByteArray { get; set; }
  public Stream SourceStream { get; set; }
  public long TotalSizeInBytes { get; set; }
  public long BytesRead { get; set; }

  public AsyncHelper(Byte[] array, Stream sourceStream, long totalSizeInBytes)
  {
    this.ByteArray = array;
    this.SourceStream = sourceStream;
    this.TotalSizeInBytes = totalSizeInBytes;
    this.BytesRead = 0;
  }
}

// Internal reading of a chunk from the stream
private void ReadAsyncCallback(IAsyncResult ar)
{
  AsyncHelper info = ar.AsyncState as AsyncHelper;
  int amountRead = 0;
  try
  {
    amountRead = info.SourceStream.EndRead(ar);
  }
  catch (IOException ex)
  {
    Trace.WriteLine(ex.Message);
    info.SourceStream.Close();
    return;
  }

  // Do something with the stream
  info.BytesRead += amountRead;
  Trace.WriteLine("info.BytesRead: " + info.BytesRead);

  if (info.SourceStream.Position < info.TotalSizeInBytes)
  {
    try
    { // Read next chunk from stream
      info.SourceStream.BeginRead(info.ByteArray, 0, info.ByteArray.Length, ReadAsyncCallback, info);
    }
    catch (IOException ex)
    {
      info.SourceStream.Close();
    }
  }
  else
  {
    info.SourceStream.Close();     
  }
}

The binding is defined like this:

BasicHttpBinding binding = new BasicHttpBinding();
binding.TransferMode = TransferMode.Streamed;
binding.MessageEncoding = WSMessageEncoding.Mtom;
binding.MaxReceivedMessageSize = 3 * 1024 * 1024;
binding.MaxBufferSize = 64 * 1024;
binding.CloseTimeout = new TimeSpan(0, 1, 0);
binding.OpenTimeout = new TimeSpan(0, 1, 0);
binding.ReceiveTimeout = new TimeSpan(0, 10, 0);
binding.SendTimeout = new TimeSpan(0, 1, 0);
binding.Security.Mode = BasicHttpSecurityMode.None;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;

回答1:


uploadRequest.Stream is a Stream that is provided by WCF. This stream probably feeds off the underlying TCP connection that WCF maintains with the client of your service.

This stream is not the same object instance that your service client has passed in. That would be impossible because client and server are only connected over TCP. They do not share the same address space so they cannot share object instances.

You are returning from UploadFile before the stream has been fully processed. WCF cannot know that a background thread of yours is still using this Stream object. So WCF releases the resources underlying the stream (probably it closes the TCP connection to the client).

WCF will close that stream once your request handling method has returned. Your async processing will then non-deterministically fail. It is a threading race between you using the stream and WCF racing to close it.

The comments under the question show that there is a misunderstanding somewhere but I'm not exactly sure what it is. If you need further clarification please leave a comment and say what you disagree with and why.



来源:https://stackoverflow.com/questions/16397534/ioexception-on-streamed-file-upload-via-wcf-over-http

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!