后台代理/HttpWebRequest流缓冲区上的发布文件不断增长



我需要从ResourceIntensiveTask中POST一个5MB文件,操作系统将最大内存使用量设置为5MB。因此,试图直接从存储中流式传输文件,但与HttpWebRequest相关的stream的大小不断增加。这是代码:

        public void writeStream(Stream writer, string filesource, string filename)
        {
            var store = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication();
            var f = store.OpenFile(filesource, FileMode.Open, FileAccess.Read);
            store.Dispose();
            byte[] buffer = Encoding.UTF8.GetBytes(String.Format(@"Content-Disposition: form-data; name=""file""; filename=""{0}""n", filename));
            writer.Write(buffer, 0, buffer.Length);
            buffer = Encoding.UTF8.GetBytes("Content-Type: application/octet-streamn");
            writer.Write(buffer, 0, buffer.Length);
            long initialMemory = Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage;
            buffer = new byte[2048]; 
            int DataRead = 0;
            do
            {
                DataRead = f.Read(buffer, 0, 2048);
                if (DataRead > 0)
                {
                    writer.Write(buffer, 0, DataRead);
                    Array.Clear(buffer, 0, 2048);
                }
            } while (DataRead > 0);
            double increasedMemory = ((double)Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage - initialMemory) / 1000000;                
            buffer = Encoding.UTF8.GetBytes("n--" + boundary + "n--");
            writer.Write(buffer, 0, buffer.Length);
            writer.Flush();
        }

increasedMemory调试变量用于在读取文件并将其流式传输到HttpWebRequest之前和之后获得差分内存,它几乎给出了文件的确切大小(5MB),这意味着进程内存正在增加5MB。

我还将AllowReadStreamBuffering=false设置为HttpWebRequest。

如何保持低内存?当内存使用限制为5MB时,如何上传大文件?

问题是,如果不能关闭写缓冲,那么直到关闭请求流后调用BeginGetResponse()(使用WireShark验证),才能连接到服务器。

我能想到的解决这个问题的唯一方法是直接使用套接字(尽管如果使用SSL连接,这会更复杂)。

这段代码适用于我,在向服务器发送数据时不会增加内存使用量。我还没有在后台任务中测试过它,但看不出它有什么不起作用的原因。

Socket _socket;
const int BUFFERSIZE = 4096;
byte[] writebuffer = new byte[BUFFERSIZE];
string hostName = "www.testdomain.com";
string hostPath = "/test/testupload.aspx";
IsolatedStorageFileStream isoFile;

public void SocketPOST(string hostName, string filesource)
{
    using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
    {
        if (store.FileExists(filesource))
        {
            isoFile = store.OpenFile(filesource, FileMode.Open, FileAccess.Read);
        }
    }
    _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    _socket.SetNetworkRequirement(NetworkSelectionCharacteristics.NonCellular);
    SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
    socketEventArg.RemoteEndPoint = new DnsEndPoint(hostName, 80);
    socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(Socket_Completed);
    _socket.ConnectAsync(socketEventArg);
}

private void Socket_Completed(object sender, SocketAsyncEventArgs e)
{
    if (e.SocketError == SocketError.Success)
    {
        switch (e.LastOperation)
        {
            case SocketAsyncOperation.Connect:   // Connected so started sending data, headers first
                if (e.ConnectSocket.Connected)
                {
                    StringBuilder sbHeaders = new StringBuilder("POST " + hostPath + " HTTP/1.1rn");
                    sbHeaders.Append("HOST: " + hostName + "rn");
                    sbHeaders.Append("USER-AGENT: MyWP7App/1.0rn");
                    sbHeaders.Append("Content-Type: text/plain; charset="utf-8"rn");
                    sbHeaders.Append("Content-Length: " + isoFile.Length.ToString() + "rnrn");
                    byte[] headerBuffer = Encoding.UTF8.GetBytes(sbHeaders.ToString());
                    e.SetBuffer(headerBuffer, 0, headerBuffer.Length);
                    if (!e.ConnectSocket.SendAsync(e)) Socket_Completed(e.ConnectSocket, e);
                }
                break;
            case SocketAsyncOperation.Send:
            case SocketAsyncOperation.SendTo:   // Previous buffer sent so send next one if stream not finished
                Array.Clear(writebuffer, 0, BUFFERSIZE);
                int DataRead = 0;
                DataRead = isoFile.Read(writebuffer, 0, BUFFERSIZE);
                if (DataRead > 0)
                {
                    e.SetBuffer(writebuffer, 0, DataRead);
                    if (!_socket.SendAsync(e)) Socket_Completed(e.ConnectSocket, e);
                }
                else
                {
                    isoFile.Dispose();
                    if (!_socket.ReceiveAsync(e)) Socket_Completed(e.ConnectSocket, e);
                }
                break;
            case SocketAsyncOperation.Receive:
            case SocketAsyncOperation.ReceiveFrom:
                if (e.BytesTransferred > 0)
                {
                    string response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred).Trim('');
                    // Check response if necessary 
                    e.ConnectSocket.Shutdown(SocketShutdown.Both);
                    e.ConnectSocket.Dispose();
                }
                break;
            default:
                break;
        }
    }
}

注意:为了简短起见,我省略了很多错误处理。

SSL注意:因为SSL在TCP级别工作,而且WP7目前不支持SSL套接字(SslStream),所以您需要自己处理证书握手、密码交换等,以便在套接字上设置SSL连接,然后使用商定的算法加密发送的所有内容(并解密接收的所有内容)。使用Bouncy Castle API已经取得了一些成功,因此这是可能的(请参阅本文)。

我注意到一件事:您忘记处理f了!

我个人会使用这样的代码:

public void writeStream(Stream writer, string filesource, string filename)
{
    using (var store = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication())
    {
        long initialMemory = Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage;
        using (var f = store.OpenFile(filesource, FileMode.Open, FileAccess.Read))
        {
            byte[] buffer = Encoding.UTF8.GetBytes(string.Format(@"Content-Disposition: form-data; name=""file""; filename=""{0}""n", filename));
            writer.Write(buffer, 0, buffer.Length);
            buffer = Encoding.UTF8.GetBytes("Content-Type: application/octet-streamn");
            writer.Write(buffer, 0, buffer.Length);
            buffer = new byte[2048];
            int DataRead = 0;
            do
            {
                DataRead = f.Read(buffer, 0, 2048);
                if (DataRead > 0)
                {
                    writer.Write(buffer, 0, DataRead);
                }
            } while (DataRead > 0);
            buffer = Encoding.UTF8.GetBytes("n--" + boundary + "n--");
            writer.Write(buffer, 0, buffer.Length);
            writer.Flush();
        }
        double increasedMemory = ((double)Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage - initialMemory) / 1000000;
    }
}

boundary变量似乎丢失了,所以这里仍然存在编码错误!

最新更新