123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- 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
- {
- /// <summary>
- /// Simple Storage Service client.
- /// </summary>
- 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;
- /// <summary>
- /// Gets or sets bucket where will saved export.
- /// </summary>
- public string CurrentBucket
- {
- get { return currentBucket; }
- set { currentBucket = value; }
- }
- /// <summary>
- /// Gets or sets filename.
- /// </summary>
- public string FileName
- {
- get { return fileName; }
- set { fileName = value; }
- }
- /// <summary>
- /// Gets or sets host S3.
- /// </summary>
- public string Host
- {
- get { return host; }
- set
- {
- host = value;
- if (host.EndsWith("/"))
- host = host.Remove(host.Length - 1);
- }
- }
- /// <inheritdoc/>
- 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);
- }
- }
- /// <summary>
- /// Get list of buckets names.
- /// </summary>
- /// <returns>List of buckets names</returns>
- public List<string> GetListBuckets()
- {
- List<string> buckets = new List<string>();
- 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;
- }
- /// <summary>
- /// Initialize signer.
- /// </summary>
- /// <param name="accessKeyId">Access key ID</param>
- /// <param name="secretAccessKey">Secret access key</param>
- /// <param name="region">Service region</param>
- public void InitSigner(string accessKeyId, string secretAccessKey, string region)
- {
- signer = new S3Signer(accessKeyId, secretAccessKey, region);
- }
- }
- }
|