using System; using System.IO; using COSXML.Log; namespace COSXML.Network { public sealed class ResponseBody { private const int SEGMENT_SIZE = 4096; private long contentLength = -1L; private string contentType; private COSXML.Callback.OnProgressCallback progressCallback; private COSXML.Callback.OnParseStream parseStream; private string filePath; private long fileOffset; private FileStream fileStream; private bool isDownload = false; private MemoryStream memoryStream; public long ContentLength { get { return contentLength; } set { contentLength = value; } } public string ContentType { get { return contentType; } set { contentType = value; } } public COSXML.Callback.OnProgressCallback ProgressCallback { get { return progressCallback; } set { progressCallback = value; } } public COSXML.Callback.OnParseStream ParseStream { get { return parseStream; } set { parseStream = value; } } public string rawContentBodyString { get; private set; } public ResponseBody() { } public ResponseBody(string filePath, long fileOffset) { this.filePath = filePath; this.fileOffset = fileOffset; this.isDownload = true; } /// /// handle cos response /// /// /// throw CosServerException /// throw Exception public void HandleResponseBody(Stream inputStream) { try { if (isDownload) { if (File.Exists(filePath) && new FileInfo(filePath).Length > fileOffset) { // 写脏文件了,直接 Truncate fileStream = new FileStream(filePath, FileMode.Truncate, FileAccess.Write); } else { // 正常文件或者追加写场景,直接写入 fileStream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write); } fileStream.Seek(fileOffset, SeekOrigin.Begin); byte[] buffer = new byte[SEGMENT_SIZE]; int recvLen = 0; long completed = recvLen; while ((recvLen = inputStream.Read(buffer, 0, buffer.Length)) != 0) { fileStream.Write(buffer, 0, recvLen); if (progressCallback != null) { completed += recvLen; progressCallback(completed, contentLength); } } fileStream.Flush(); } else { if ("application/xml".Equals(contentType, StringComparison.OrdinalIgnoreCase) && contentLength > 0 && contentLength < 10 * 1000) { // save raw content memoryStream = new MemoryStream((int)contentLength); //inputStream.CopyTo(memoryStream); byte[] buffer = new byte[10 * 1000]; int count; while ((count = inputStream.Read(buffer, 0, buffer.Length)) != 0) { memoryStream.Write(buffer, 0, count); } rawContentBodyString = System.Text.Encoding.UTF8.GetString(memoryStream.ToArray()); memoryStream.Seek(0, SeekOrigin.Begin); inputStream = memoryStream; } if (parseStream != null) { parseStream(inputStream, contentType, contentLength); } } } catch (Exception ex) { QLog.Error("ResponseBody", ex.Message, ex); throw; } finally { if (inputStream != null) { inputStream.Close(); inputStream.Dispose(); } if (fileStream != null) { fileStream.Close(); fileStream.Dispose(); } //if (memoryStream != null) memoryStream.Close(); } } public void StartHandleResponseBody(Stream inputStream, EndResponseBody endResponseBody) { ResponseBodyState responseBodyState = new ResponseBodyState(); responseBodyState.inputStream = inputStream; responseBodyState.endResponseBody = endResponseBody; responseBodyState.completed = 0L; try { int count = (int)((contentLength > SEGMENT_SIZE || contentLength <= 0) ? SEGMENT_SIZE : contentLength); byte[] buffer = new byte[count]; responseBodyState.buffer = buffer; if (isDownload) { if (File.Exists(filePath) && new FileInfo(filePath).Length > fileOffset) { // 写脏文件了,直接 Truncate fileStream = new FileStream(filePath, FileMode.Truncate, FileAccess.Write); } else { // 正常文件或者追加写场景,直接写入 fileStream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write); } fileStream.Seek(fileOffset, SeekOrigin.Begin); responseBodyState.inputStream.BeginRead(responseBodyState.buffer, 0, responseBodyState.buffer.Length, AsyncStreamCallback, responseBodyState); } else { memoryStream = new MemoryStream(count); responseBodyState.buffer = buffer; responseBodyState.inputStream.BeginRead(responseBodyState.buffer, 0, responseBodyState.buffer.Length, AsyncStreamCallback, responseBodyState); } } catch (Exception ex) { if (fileStream != null) { fileStream.Close(); fileStream.Dispose(); } if (memoryStream != null) { memoryStream.Close(); memoryStream.Dispose(); } responseBodyState.endResponseBody(false, ex); responseBodyState.Clear(); QLog.Error("ResponseBody", ex.Message, ex); } } private void AsyncStreamCallback(IAsyncResult ar) { ResponseBodyState responseBodyState = ar.AsyncState as ResponseBodyState; Stream inputStream = responseBodyState.inputStream; try { int recvLen = inputStream.EndRead(ar); responseBodyState.completed += recvLen; if (recvLen > 0) { if (isDownload) { fileStream.Write(responseBodyState.buffer, 0, recvLen); if (progressCallback != null) { progressCallback(responseBodyState.completed, contentLength); } } else { memoryStream.Write(responseBodyState.buffer, 0, recvLen); } inputStream.BeginRead(responseBodyState.buffer, 0, responseBodyState.buffer.Length, AsyncStreamCallback, responseBodyState); } else if (recvLen == 0) { if (isDownload) { fileStream.Flush(); } else { if ("application/xml".Equals(contentType, StringComparison.OrdinalIgnoreCase) && memoryStream.Length > 0 && memoryStream.Length < 10 * 1000) { memoryStream.Seek(0, SeekOrigin.Begin); rawContentBodyString = System.Text.Encoding.UTF8.GetString(memoryStream.ToArray()); } memoryStream.Seek(0, SeekOrigin.Begin); parseStream(memoryStream, contentType, responseBodyState.completed); } if (fileStream != null) { fileStream.Close(); fileStream.Dispose(); } if (memoryStream != null) { memoryStream.Close(); memoryStream.Dispose(); } responseBodyState.endResponseBody(true, null); responseBodyState.Clear(); } } catch (Exception ex) { if (fileStream != null) { fileStream.Close(); fileStream.Dispose(); } if (memoryStream != null) { memoryStream.Close(); memoryStream.Dispose(); } responseBodyState.endResponseBody(false, ex); responseBodyState.Clear(); QLog.Error("ResponseBody", ex.Message, ex); } } } public delegate void EndResponseBody(bool isSuccess, Exception ex); public class ResponseBodyState { public Stream inputStream; public byte[] buffer; public long completed; public EndResponseBody endResponseBody; public void Clear() { if (inputStream != null) { inputStream.Close(); } if (buffer != null) { buffer = null; } } } }