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); } } }