using FastReport.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
namespace FastReport.Cloud.StorageClient.S3
{
///
/// Simple Storage Service client.
///
public class S3StorageClient : CloudStorageClient
{
private int chunkLength = 1 * 1024 * 1024; // 10MB
private int timeout = 8000000;
private string currentBucket;
private string fileName;
private string host;
private S3Signer signer;
public event EventHandler AfterUpload;
public event EventHandler BeforeUpload;
///
/// Gets or sets bucket where will saved export.
///
public string CurrentBucket
{
get { return currentBucket; }
set { currentBucket = value; }
}
///
/// Gets or sets filename.
///
public string FileName
{
get { return fileName; }
set { fileName = value; }
}
///
/// Gets or sets host S3.
///
public string Host
{
get { return host; }
set
{
host = value;
if (host.EndsWith("/"))
host = host.Remove(host.Length - 1);
}
}
///
protected override void SaveMemoryStream(MemoryStream ms)
{
try
{
ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00);
ms.Position = 0;
if (ms.Length <= chunkLength)
{
BeforeUpload?.Invoke(this, null);
HttpWebRequest httpRequest = CreatePut($"{host}/{currentBucket}/{fileName}");
httpRequest.Headers.Add(HttpRequestHeader.ContentMd5, Convert.ToBase64String(MD5.Create().ComputeHash(ms)));
ms.Position = 0;
ms.CopyTo(httpRequest.GetRequestStream());
ms.Position = 0;
signer.Sign(httpRequest, ms);
var response = httpRequest.GetResponse();
AfterUpload?.Invoke(this, null);
}
else
{
BeforeUpload?.Invoke(this, null);
MemoryStream memoryStream = new MemoryStream();
ms.CopyTo(memoryStream);
Task.Factory.StartNew(() =>
{
HttpWebRequest httpRequest = CreatePut($"{host}/{currentBucket}/{fileName}");
httpRequest.SendChunked = true;
httpRequest.ReadWriteTimeout = 32000;
httpRequest.Timeout = timeout;
memoryStream.Position = 0;
httpRequest.Headers.Add(HttpRequestHeader.ContentMd5, Convert.ToBase64String(MD5.Create().ComputeHash(memoryStream)));
memoryStream.Position = 0;
SendChunked(httpRequest, memoryStream, signer);
AfterUpload?.Invoke(this, null);
memoryStream.Close();
});
}
}
catch (Exception ex)
{
throw new CloudStorageException(ex.Message, ex);
}
}
private HttpWebRequest CreatePut(string url)
{
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(url);
httpRequest.Method = WebRequestMethods.Http.Put;
return httpRequest;
}
private void SendChunked(HttpWebRequest httpRequest, MemoryStream ms, S3Signer signer)
{
try
{
var data = DateTimeOffset.UtcNow;
string previousSign = signer.SignSeed(httpRequest, ms.Length, data);
int chunkLength = this.chunkLength;
Stream requestStream = httpRequest.GetRequestStream();
int chunkCount = (int)Math.Ceiling((decimal)ms.Length / chunkLength);
int currentCunkLength = chunkLength;
for (int i = 0; i < chunkCount; i++)
{
if (i == chunkCount - 1 && ms.Length % chunkLength > 0)
currentCunkLength = (int)(ms.Length % chunkLength);
using (MemoryStream chunkData = new MemoryStream())
{
for (int j = 0; j < currentCunkLength; j++)
{
chunkData.WriteByte((byte)ms.ReadByte());
}
chunkData.Position = 0;
previousSign = signer.SignChunk(httpRequest, previousSign, chunkData, requestStream, data);
}
}
signer.SignChunk(httpRequest, previousSign, null, requestStream, data);
WebResponse response = httpRequest.GetResponse();
requestStream.Close();
GC.Collect();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, Res.Get("Messages,Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
///
/// Get list of buckets names.
///
/// List of buckets names
public List GetListBuckets()
{
List buckets = new List();
try
{
ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00);
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(host + "/");
signer.Sign(httpRequest);
var response = httpRequest.GetResponse();
System.Xml.XmlDocument xmlReader = new System.Xml.XmlDocument();
xmlReader.Load(response.GetResponseStream());
foreach (XmlNode node in xmlReader.LastChild["Buckets"].ChildNodes)
buckets.Add(node["Name"].InnerText);
}
catch (System.Exception ex)
{
throw new CloudStorageException(ex.Message, ex);
}
return buckets;
}
///
/// Initialize signer.
///
/// Access key ID
/// Secret access key
/// Service region
public void InitSigner(string accessKeyId, string secretAccessKey, string region)
{
signer = new S3Signer(accessKeyId, secretAccessKey, region);
}
}
}