S3Client.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. using FastReport.Utils;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Net;
  6. using System.Security.Cryptography;
  7. using System.Threading.Tasks;
  8. using System.Windows.Forms;
  9. using System.Xml;
  10. namespace FastReport.Cloud.StorageClient.S3
  11. {
  12. /// <summary>
  13. /// Simple Storage Service client.
  14. /// </summary>
  15. public class S3StorageClient : CloudStorageClient
  16. {
  17. private int chunkLength = 1 * 1024 * 1024; // 10MB
  18. private int timeout = 8000000;
  19. private string currentBucket;
  20. private string fileName;
  21. private string host;
  22. private S3Signer signer;
  23. public event EventHandler AfterUpload;
  24. public event EventHandler BeforeUpload;
  25. /// <summary>
  26. /// Gets or sets bucket where will saved export.
  27. /// </summary>
  28. public string CurrentBucket
  29. {
  30. get { return currentBucket; }
  31. set { currentBucket = value; }
  32. }
  33. /// <summary>
  34. /// Gets or sets filename.
  35. /// </summary>
  36. public string FileName
  37. {
  38. get { return fileName; }
  39. set { fileName = value; }
  40. }
  41. /// <summary>
  42. /// Gets or sets host S3.
  43. /// </summary>
  44. public string Host
  45. {
  46. get { return host; }
  47. set
  48. {
  49. host = value;
  50. if (host.EndsWith("/"))
  51. host = host.Remove(host.Length - 1);
  52. }
  53. }
  54. /// <inheritdoc/>
  55. protected override void SaveMemoryStream(MemoryStream ms)
  56. {
  57. try
  58. {
  59. ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00);
  60. ms.Position = 0;
  61. if (ms.Length <= chunkLength)
  62. {
  63. BeforeUpload?.Invoke(this, null);
  64. HttpWebRequest httpRequest = CreatePut($"{host}/{currentBucket}/{fileName}");
  65. httpRequest.Headers.Add(HttpRequestHeader.ContentMd5, Convert.ToBase64String(MD5.Create().ComputeHash(ms)));
  66. ms.Position = 0;
  67. ms.CopyTo(httpRequest.GetRequestStream());
  68. ms.Position = 0;
  69. signer.Sign(httpRequest, ms);
  70. var response = httpRequest.GetResponse();
  71. AfterUpload?.Invoke(this, null);
  72. }
  73. else
  74. {
  75. BeforeUpload?.Invoke(this, null);
  76. MemoryStream memoryStream = new MemoryStream();
  77. ms.CopyTo(memoryStream);
  78. Task.Factory.StartNew(() =>
  79. {
  80. HttpWebRequest httpRequest = CreatePut($"{host}/{currentBucket}/{fileName}");
  81. httpRequest.SendChunked = true;
  82. httpRequest.ReadWriteTimeout = 32000;
  83. httpRequest.Timeout = timeout;
  84. memoryStream.Position = 0;
  85. httpRequest.Headers.Add(HttpRequestHeader.ContentMd5, Convert.ToBase64String(MD5.Create().ComputeHash(memoryStream)));
  86. memoryStream.Position = 0;
  87. SendChunked(httpRequest, memoryStream, signer);
  88. AfterUpload?.Invoke(this, null);
  89. memoryStream.Close();
  90. });
  91. }
  92. }
  93. catch (Exception ex)
  94. {
  95. throw new CloudStorageException(ex.Message, ex);
  96. }
  97. }
  98. private HttpWebRequest CreatePut(string url)
  99. {
  100. HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(url);
  101. httpRequest.Method = WebRequestMethods.Http.Put;
  102. return httpRequest;
  103. }
  104. private void SendChunked(HttpWebRequest httpRequest, MemoryStream ms, S3Signer signer)
  105. {
  106. try
  107. {
  108. var data = DateTimeOffset.UtcNow;
  109. string previousSign = signer.SignSeed(httpRequest, ms.Length, data);
  110. int chunkLength = this.chunkLength;
  111. Stream requestStream = httpRequest.GetRequestStream();
  112. int chunkCount = (int)Math.Ceiling((decimal)ms.Length / chunkLength);
  113. int currentCunkLength = chunkLength;
  114. for (int i = 0; i < chunkCount; i++)
  115. {
  116. if (i == chunkCount - 1 && ms.Length % chunkLength > 0)
  117. currentCunkLength = (int)(ms.Length % chunkLength);
  118. using (MemoryStream chunkData = new MemoryStream())
  119. {
  120. for (int j = 0; j < currentCunkLength; j++)
  121. {
  122. chunkData.WriteByte((byte)ms.ReadByte());
  123. }
  124. chunkData.Position = 0;
  125. previousSign = signer.SignChunk(httpRequest, previousSign, chunkData, requestStream, data);
  126. }
  127. }
  128. signer.SignChunk(httpRequest, previousSign, null, requestStream, data);
  129. WebResponse response = httpRequest.GetResponse();
  130. requestStream.Close();
  131. GC.Collect();
  132. }
  133. catch (Exception ex)
  134. {
  135. MessageBox.Show(ex.Message, Res.Get("Messages,Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
  136. }
  137. }
  138. /// <summary>
  139. /// Get list of buckets names.
  140. /// </summary>
  141. /// <returns>List of buckets names</returns>
  142. public List<string> GetListBuckets()
  143. {
  144. List<string> buckets = new List<string>();
  145. try
  146. {
  147. ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00);
  148. HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(host + "/");
  149. signer.Sign(httpRequest);
  150. var response = httpRequest.GetResponse();
  151. System.Xml.XmlDocument xmlReader = new System.Xml.XmlDocument();
  152. xmlReader.Load(response.GetResponseStream());
  153. foreach (XmlNode node in xmlReader.LastChild["Buckets"].ChildNodes)
  154. buckets.Add(node["Name"].InnerText);
  155. }
  156. catch (System.Exception ex)
  157. {
  158. throw new CloudStorageException(ex.Message, ex);
  159. }
  160. return buckets;
  161. }
  162. /// <summary>
  163. /// Initialize signer.
  164. /// </summary>
  165. /// <param name="accessKeyId">Access key ID</param>
  166. /// <param name="secretAccessKey">Secret access key</param>
  167. /// <param name="region">Service region</param>
  168. public void InitSigner(string accessKeyId, string secretAccessKey, string region)
  169. {
  170. signer = new S3Signer(accessKeyId, secretAccessKey, region);
  171. }
  172. }
  173. }