using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using COSXML.Common;
using COSXML.Log;
using COSXML.Utils;
namespace COSXML.Network
{
///
/// request body for http request
///
public abstract class RequestBody
{
protected static string TAG = typeof(RequestBody).Name;
// 64kb
public const int SEGMENT_SIZE = 64 * 1024;
protected long contentLength;
protected string contentType;
protected Callback.OnProgressCallback progressCallback;
///
/// body length
///
public virtual long ContentLength
{
get
{
return contentLength;
}
set { contentLength = value; }
}
///
/// body mime type
///
public virtual string ContentType
{
get
{
return contentType;
}
set { contentType = value; }
}
///
/// calculation content md5
///
///
public virtual string GetMD5()
{
throw new NotImplementedException();
}
///
/// Synchronization method: write data to outputStream
///
/// output stream for writing data
public abstract void OnWrite(Stream outputStream);
///
/// Asynchronous method: handle request body
///
///
///
public abstract void StartHandleRequestBody(Stream outputStream, EndRequestBody endRequestBody);
public Callback.OnProgressCallback ProgressCallback
{
get
{
return progressCallback;
}
set { progressCallback = value; }
}
///
/// notify progress is complete!
///
internal void OnNotifyGetResponse()
{
if (progressCallback != null && contentLength >= 0)
{
progressCallback(contentLength, contentLength);
}
}
///
/// calculation progress
///
///
///
protected void UpdateProgress(long complete, long total)
{
if (total == 0)
{
progressCallback(0, 0);
}
else if (complete < total)
{
progressCallback(complete, total);
}
else
{
progressCallback(total - 1, total);
}
}
}
public class RequestBodyState
{
public byte[] buffer;
public long complete;
public Stream outputStream;
public EndRequestBody endRequestBody;
}
public delegate void EndRequestBody(Exception exception);
public class ByteRequestBody : RequestBody
{
private readonly byte[] data;
//private RequestBodyState requestBodyState;
public ByteRequestBody(byte[] data)
{
this.data = data;
contentLength = data.Length;
}
public override void OnWrite(Stream outputStream)
{
StartHandleRequestBody(outputStream);
}
public override string GetMD5()
{
return DigestUtils.GetMd5ToBase64(data);
}
public override void StartHandleRequestBody(Stream outputStream, EndRequestBody endRequestBody = null)
{
try
{
int completed = 0;
while (completed + SEGMENT_SIZE < contentLength)
{
outputStream.Write(data, completed, SEGMENT_SIZE);
outputStream.Flush();
completed += SEGMENT_SIZE;
if (progressCallback != null)
{
UpdateProgress(completed, contentLength);
}
}
if (completed < contentLength)
{
//包括本身
//包括本身
outputStream.Write(data, completed, (int)(contentLength - completed));
outputStream.Flush();
if (progressCallback != null)
{
UpdateProgress(contentLength, contentLength);
}
}
if (endRequestBody != null)
{
endRequestBody(null);
}
}
catch (Exception ex)
{
if (endRequestBody != null)
{
endRequestBody(ex);
}
else
{
throw;
}
}
finally
{
if (outputStream != null)
{
outputStream.Flush();
outputStream.Close();
outputStream.Dispose();
//QLog.D("XIAO", "stream close");
outputStream = null;
}
}
}
}
public class FileRequestBody : RequestBody
{
private readonly string srcPath;
private readonly long fileOffset;
//private RequestBodyState requestBodyState;
private FileStream fileStream;
public FileRequestBody(string srcPath, long fileOffset, long sendContentSize)
{
this.srcPath = srcPath;
this.fileOffset = fileOffset;
contentLength = sendContentSize;
}
public override void OnWrite(Stream outputStream)
{
StartHandleRequestBody(outputStream);
}
public override string GetMD5()
{
try
{
fileStream = new FileStream(srcPath, FileMode.Open, FileAccess.Read);
fileStream.Seek(fileOffset, SeekOrigin.Begin);
return DigestUtils.GetMd5ToBase64(fileStream, contentLength);
}
catch (Exception ex)
{
QLog.Error(TAG, ex.Message, ex);
throw;
}
finally
{
if (fileStream != null)
{
fileStream.Close();
}
}
}
public override void StartHandleRequestBody(Stream outputStream, EndRequestBody endRequestBody = null)
{
FileStream fileStream = null;
try
{
byte[] buffer = new byte[SEGMENT_SIZE];
int bytesRead = 0;
long completed = bytesRead;
fileStream = new FileStream(srcPath, FileMode.Open, FileAccess.Read);
//seek to designated position
//seek to designated position
fileStream.Seek(fileOffset, SeekOrigin.Begin);
long remain = contentLength - completed;
if (remain > 0)
{
while ((bytesRead = fileStream.Read(buffer, 0, (int)(buffer.Length > remain ? remain : buffer.Length))) != 0)
{
outputStream.Write(buffer, 0, bytesRead);
outputStream.Flush();
completed += bytesRead;
if (progressCallback != null)
{
UpdateProgress(completed, contentLength);
}
remain = contentLength - completed;
if (remain == 0)
{
break;
}
}
}
else
{
if (progressCallback != null)
{
UpdateProgress(completed, contentLength);
}
}
buffer = null;
if (endRequestBody != null)
{
endRequestBody(null);
}
}
catch (Exception ex)
{
if (endRequestBody != null)
{
endRequestBody(ex);
}
else
{
throw;
}
}
finally
{
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
//QLog.D("XIAO", "stream close");
fileStream = null;
}
if (outputStream != null)
{
outputStream.Flush();
outputStream.Close();
outputStream.Dispose();
//QLog.D("XIAO", "stream close");
outputStream = null;
}
}
}
}
public class StreamRequestBody : RequestBody
{
private readonly long fileOffset;
//private RequestBodyState requestBodyState;
private Stream stream;
public StreamRequestBody(Stream stream, long fileOffset, long sendContentSize)
{
this.stream = stream;
this.fileOffset = fileOffset;
contentLength = sendContentSize;
}
public override void OnWrite(Stream outputStream)
{
StartHandleRequestBody(outputStream);
}
public override string GetMD5()
{
try
{
stream.Seek(fileOffset, SeekOrigin.Begin);
return DigestUtils.GetMd5ToBase64(stream, contentLength);
}
catch (Exception ex)
{
QLog.Error(TAG, ex.Message, ex);
throw;
}
}
public override void StartHandleRequestBody(Stream outputStream, EndRequestBody endRequestBody = null)
{
try
{
byte[] buffer = new byte[SEGMENT_SIZE];
int bytesRead = 0;
long completed = bytesRead;
//seek to designated position
//seek to designated position
stream.Seek(fileOffset, SeekOrigin.Begin);
long remain = contentLength - completed;
if (remain > 0)
{
while ((bytesRead = stream.Read(buffer, 0, (int)(buffer.Length > remain ? remain : buffer.Length))) != 0)
{
outputStream.Write(buffer, 0, bytesRead);
outputStream.Flush();
completed += bytesRead;
if (progressCallback != null)
{
UpdateProgress(completed, contentLength);
}
remain = contentLength - completed;
if (remain == 0)
{
break;
}
}
}
else
{
if (progressCallback != null)
{
UpdateProgress(completed, contentLength);
}
}
buffer = null;
if (endRequestBody != null)
{
endRequestBody(null);
}
}
catch (Exception ex)
{
if (endRequestBody != null)
{
endRequestBody(ex);
}
else
{
throw;
}
}
finally
{
if (outputStream != null)
{
outputStream.Flush();
outputStream.Close();
outputStream.Dispose();
//QLog.D("XIAO", "stream close");
outputStream = null;
}
}
}
}
public class FileStreamRequestBody : RequestBody
{
private readonly long fileOffset;
//private RequestBodyState requestBodyState;
private FileStream fileStream;
public FileStreamRequestBody(FileStream fileStream, long fileOffset, long sendContentSize)
{
this.fileStream = fileStream;
this.fileOffset = fileOffset;
contentLength = sendContentSize;
}
public override void OnWrite(Stream outputStream)
{
StartHandleRequestBody(outputStream);
}
public override string GetMD5()
{
try
{
fileStream.Seek(fileOffset, SeekOrigin.Begin);
return DigestUtils.GetMd5ToBase64(fileStream, contentLength);
}
catch (Exception ex)
{
QLog.Error(TAG, ex.Message, ex);
throw;
}
}
public override void StartHandleRequestBody(Stream outputStream, EndRequestBody endRequestBody = null)
{
try
{
byte[] buffer = new byte[SEGMENT_SIZE];
int bytesRead = 0;
long completed = bytesRead;
//seek to designated position
//seek to designated position
fileStream.Seek(fileOffset, SeekOrigin.Begin);
long remain = contentLength - completed;
if (remain > 0)
{
while ((bytesRead = fileStream.Read(buffer, 0, (int)(buffer.Length > remain ? remain : buffer.Length))) != 0)
{
outputStream.Write(buffer, 0, bytesRead);
outputStream.Flush();
completed += bytesRead;
if (progressCallback != null)
{
UpdateProgress(completed, contentLength);
}
remain = contentLength - completed;
if (remain == 0)
{
break;
}
}
}
else
{
if (progressCallback != null)
{
UpdateProgress(completed, contentLength);
}
}
buffer = null;
if (endRequestBody != null)
{
endRequestBody(null);
}
}
catch (Exception ex)
{
if (endRequestBody != null)
{
endRequestBody(ex);
}
else
{
throw;
}
}
finally
{
if (outputStream != null)
{
outputStream.Flush();
outputStream.Close();
outputStream.Dispose();
//QLog.D("XIAO", "stream close");
outputStream = null;
}
}
}
}
public class MultipartRequestBody : RequestBody
{
private readonly string DASHDASH = "--";
public static string BOUNDARY = "314159265358979323------------";
private readonly string CRLF = "\r\n";
private readonly string CONTENT_DISPOSITION = "Content-Disposition: form-data; ";
private Dictionary parameters;
private string name;
private string fileName;
private byte[] data;
private string srcPath;
private long fileOffset;
private Stream fileStream;
private long realContentLength;
private RequestBodyState requestBodyState;
public MultipartRequestBody()
{
contentType = "multipart/form-data; boundary=" + BOUNDARY;
parameters = new Dictionary();
contentLength = -1L;
}
public void AddParamters(Dictionary parameters)
{
if (parameters != null)
{
foreach (KeyValuePair pair in parameters)
{
this.parameters.Add(pair.Key, pair.Value);
}
}
}
public void AddParameter(string key, string value)
{
if (key != null)
{
parameters.Add(key, value);
}
}
public void AddData(byte[] data, string name, string fileName)
{
this.data = data;
this.name = name;
this.fileName = fileName;
this.realContentLength = data.Length;
}
public void AddData(string srcPath, long fileOffset, long sendContentSize, string name, string fileName)
{
this.srcPath = srcPath;
this.fileOffset = fileOffset;
this.name = name;
this.fileName = fileName;
realContentLength = sendContentSize;
}
//计算长度
public override long ContentLength
{
get
{
ComputerContentLength();
return base.ContentLength;
}
}
private void ComputerContentLength()
{
if (contentLength != -1)
{
return;
}
contentLength = 0;
if (parameters != null && parameters.Count > 0)
{
StringBuilder parametersBuilder = new StringBuilder();
foreach (KeyValuePair pair in parameters)
{
parametersBuilder.Append(DASHDASH).Append(BOUNDARY).Append(CRLF);
parametersBuilder.Append(CONTENT_DISPOSITION).Append("name=\"").Append(pair.Key).Append("\"").Append(CRLF);
parametersBuilder.Append(CRLF);
parametersBuilder.Append(pair.Value).Append(CRLF);
}
string content = parametersBuilder.ToString();
byte[] data = Encoding.UTF8.GetBytes(content);
contentLength += data.Length;
}
if (name != null)
{
StringBuilder parametersBuilder = new StringBuilder();
parametersBuilder.Append(DASHDASH).Append(BOUNDARY).Append(CRLF);
parametersBuilder.Append(CONTENT_DISPOSITION).Append("name=\"").Append(name).Append("\"");
if (!String.IsNullOrEmpty(fileName))
{
parametersBuilder.Append("; filename=").Append("\"").Append(fileName).Append("\"");
}
parametersBuilder.Append(CRLF);
parametersBuilder.Append("Content-Type: ").Append("application/octet-stream").Append(CRLF);
parametersBuilder.Append(CRLF);
string content = parametersBuilder.ToString();
byte[] data = Encoding.UTF8.GetBytes(content);
contentLength += data.Length;
}
contentLength += realContentLength;
string endLine = CRLF + DASHDASH + BOUNDARY + DASHDASH + CRLF;
byte[] endData = Encoding.UTF8.GetBytes(endLine);
contentLength += endData.Length;
}
private void WriteParameters(Stream outputStream)
{
if (parameters != null && parameters.Count > 0)
{
StringBuilder parametersBuilder = new StringBuilder();
foreach (KeyValuePair pair in parameters)
{
parametersBuilder.Append(DASHDASH).Append(BOUNDARY).Append(CRLF);
parametersBuilder.Append(CONTENT_DISPOSITION).Append("name=\"").Append(pair.Key).Append("\"").Append(CRLF);
parametersBuilder.Append(CRLF);
parametersBuilder.Append(pair.Value).Append(CRLF);
}
string content = parametersBuilder.ToString();
byte[] data = Encoding.UTF8.GetBytes(content);
outputStream.Write(data, 0, data.Length);
}
}
private void WriteFileParameters(Stream outputStream)
{
StringBuilder parametersBuilder = new StringBuilder();
parametersBuilder.Append(DASHDASH).Append(BOUNDARY).Append(CRLF);
parametersBuilder.Append(CONTENT_DISPOSITION).Append("name=\"").Append(name).Append("\"");
if (!String.IsNullOrEmpty(fileName))
{
parametersBuilder.Append("; filename=").Append("\"").Append(fileName).Append("\"");
}
parametersBuilder.Append(CRLF);
parametersBuilder.Append("Content-Type: ").Append("application/octet-stream").Append(CRLF);
parametersBuilder.Append(CRLF);
string content = parametersBuilder.ToString();
byte[] data = Encoding.UTF8.GetBytes(content);
outputStream.Write(data, 0, data.Length);
}
private void WriteEndLine(Stream outputStream)
{
string endLine = CRLF + DASHDASH + BOUNDARY + DASHDASH + CRLF;
byte[] data = Encoding.UTF8.GetBytes(endLine);
outputStream.Write(data, 0, data.Length);
}
public override void OnWrite(Stream outputStream)
{
StartHandleRequestBody(outputStream);
}
public override void StartHandleRequestBody(Stream outputStream, EndRequestBody endRequestBody = null)
{
//write paramters
WriteParameters(outputStream);
//写入content-disposition: form-data; name = "file"; filename = "xxx"\r\n
WriteFileParameters(outputStream);
outputStream.Flush();
//wrtie content: file or bintary
try
{
if (data != null)
{
int completed = 0;
while (completed + SEGMENT_SIZE < realContentLength)
{
outputStream.Write(data, completed, SEGMENT_SIZE);
outputStream.Flush();
completed += SEGMENT_SIZE;
if (progressCallback != null)
{
UpdateProgress(completed, realContentLength);
}
}
if (completed < realContentLength)
{
//包括本身
//包括本身
outputStream.Write(data, completed, (int)(realContentLength - completed));
if (progressCallback != null)
{
UpdateProgress(realContentLength, realContentLength);
}
}
WriteEndLine(outputStream);
outputStream.Flush();
}
else if (srcPath != null)
{
// 64kb
// 64kb
byte[] buffer = new byte[SEGMENT_SIZE];
int bytesRead = 0;
long completed = bytesRead;
fileStream = new FileStream(srcPath, FileMode.Open, FileAccess.Read);
fileStream.Seek(fileOffset, SeekOrigin.Begin);
long remain = realContentLength - completed;
if (remain > 0)
{
while ((bytesRead = fileStream.Read(buffer, 0, (int)(buffer.Length > remain ? remain : buffer.Length))) != 0)
{
outputStream.Write(buffer, 0, bytesRead);
outputStream.Flush();
completed += bytesRead;
if (progressCallback != null)
{
UpdateProgress(completed, realContentLength);
}
remain = realContentLength - completed;
if (remain == 0)
{
break;
}
}
}
else
{
if (progressCallback != null)
{
completed += bytesRead;
UpdateProgress(completed, realContentLength);
}
}
WriteEndLine(outputStream);
outputStream.Flush();
}
if (endRequestBody != null)
{
endRequestBody(null);
}
}
catch (Exception ex)
{
if (endRequestBody != null)
{
endRequestBody(ex);
}
else
{
throw;
}
}
finally
{
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
fileStream = null;
}
if (outputStream != null)
{
outputStream.Flush();
outputStream.Close();
outputStream.Dispose();
outputStream = null;
}
}
}
}
}