2022-03-15 18:00:53 +00:00
|
|
|
// (c) Copyright Ascensio System SIA 2010-2022
|
|
|
|
//
|
|
|
|
// This program is a free software product.
|
|
|
|
// You can redistribute it and/or modify it under the terms
|
|
|
|
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
|
|
|
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
|
|
|
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
|
|
|
// any third-party rights.
|
|
|
|
//
|
|
|
|
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
|
|
|
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
|
|
|
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
|
|
|
//
|
|
|
|
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
|
|
|
//
|
|
|
|
// The interactive user interfaces in modified source and object code versions of the Program must
|
|
|
|
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
|
|
|
//
|
|
|
|
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
|
|
|
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
|
|
|
// trademark law for use of our trademarks.
|
|
|
|
//
|
|
|
|
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
|
|
|
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
|
|
|
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
namespace ASC.Data.Storage.GoogleCloud;
|
|
|
|
|
|
|
|
[Scope]
|
|
|
|
public class GoogleCloudStorage : BaseStorage
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
public override bool IsSupportChunking => true;
|
|
|
|
|
|
|
|
private string _subDir = string.Empty;
|
|
|
|
private Dictionary<string, PredefinedObjectAcl> _domainsAcl;
|
|
|
|
private PredefinedObjectAcl _moduleAcl;
|
|
|
|
private string _bucket = string.Empty;
|
|
|
|
private string _json = string.Empty;
|
|
|
|
private Uri _bucketRoot;
|
|
|
|
private Uri _bucketSSlRoot;
|
|
|
|
private bool _lowerCasing = true;
|
|
|
|
|
|
|
|
public GoogleCloudStorage(
|
|
|
|
TempStream tempStream,
|
|
|
|
TenantManager tenantManager,
|
|
|
|
PathUtils pathUtils,
|
|
|
|
EmailValidationKeyProvider emailValidationKeyProvider,
|
|
|
|
IHttpContextAccessor httpContextAccessor,
|
2022-04-26 17:53:48 +00:00
|
|
|
ILoggerProvider factory,
|
|
|
|
ILogger<GoogleCloudStorage> options,
|
|
|
|
IHttpClientFactory clientFactory)
|
|
|
|
: base(tempStream, tenantManager, pathUtils, emailValidationKeyProvider, httpContextAccessor, factory, options, clientFactory)
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2022-02-10 10:43:28 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
public override IDataStore Configure(string tenant, Handler handlerConfig, Module moduleConfig, IDictionary<string, string> props)
|
|
|
|
{
|
2022-02-11 10:04:06 +00:00
|
|
|
Tenant = tenant;
|
2021-11-24 19:34:39 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (moduleConfig != null)
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-11 10:04:06 +00:00
|
|
|
Modulename = moduleConfig.Name;
|
|
|
|
DataList = new DataList(moduleConfig);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-11 10:04:06 +00:00
|
|
|
DomainsExpires = moduleConfig.Domain.Where(x => x.Expires != TimeSpan.Zero).ToDictionary(x => x.Name, y => y.Expires);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-11 10:04:06 +00:00
|
|
|
DomainsExpires.Add(string.Empty, moduleConfig.Expires);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
_domainsAcl = moduleConfig.Domain.ToDictionary(x => x.Name, y => GetGoogleCloudAcl(y.Acl));
|
|
|
|
_moduleAcl = GetGoogleCloudAcl(moduleConfig.Acl);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-02-11 10:04:06 +00:00
|
|
|
Modulename = string.Empty;
|
|
|
|
DataList = null;
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-11 10:04:06 +00:00
|
|
|
DomainsExpires = new Dictionary<string, TimeSpan> { { string.Empty, TimeSpan.Zero } };
|
2022-02-10 11:24:16 +00:00
|
|
|
_domainsAcl = new Dictionary<string, PredefinedObjectAcl>();
|
|
|
|
_moduleAcl = PredefinedObjectAcl.PublicRead;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
_bucket = props["bucket"];
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
_bucketRoot = props.ContainsKey("cname") && Uri.IsWellFormedUriString(props["cname"], UriKind.Absolute)
|
|
|
|
? new Uri(props["cname"], UriKind.Absolute)
|
2022-01-14 13:12:37 +00:00
|
|
|
: new Uri("https://storage.googleapis.com/" + _bucket + "/", UriKind.Absolute);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
_bucketSSlRoot = props.ContainsKey("cnamessl") &&
|
|
|
|
Uri.IsWellFormedUriString(props["cnamessl"], UriKind.Absolute)
|
|
|
|
? new Uri(props["cnamessl"], UriKind.Absolute)
|
2022-01-14 13:12:37 +00:00
|
|
|
: new Uri("https://storage.googleapis.com/" + _bucket + "/", UriKind.Absolute);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-16 09:47:11 +00:00
|
|
|
if (props.TryGetValue("lower", out var value))
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-02-16 09:47:11 +00:00
|
|
|
bool.TryParse(value, out _lowerCasing);
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
_json = props["json"];
|
2019-08-15 12:04:42 +00:00
|
|
|
|
2022-02-16 09:47:11 +00:00
|
|
|
props.TryGetValue("subdir", out _subDir);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
return this;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
public static long DateToUnixTimestamp(DateTime date)
|
|
|
|
{
|
|
|
|
var ts = date - new DateTime(1970, 1, 1, 0, 0, 0);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
return (long)ts.TotalSeconds;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override Task<Uri> GetInternalUriAsync(string domain, string path, TimeSpan expire, IEnumerable<string> headers)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
if (expire == TimeSpan.Zero || expire == TimeSpan.MinValue || expire == TimeSpan.MaxValue)
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
expire = GetExpire(domain);
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
2022-02-10 11:24:16 +00:00
|
|
|
if (expire == TimeSpan.Zero || expire == TimeSpan.MinValue || expire == TimeSpan.MaxValue)
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
return Task.FromResult(GetUriShared(domain, path));
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
return InternalGetInternalUriAsync(domain, path, expire);
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
private async Task<Uri> InternalGetInternalUriAsync(string domain, string path, TimeSpan expire)
|
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(_json ?? ""));
|
2022-04-15 09:08:06 +00:00
|
|
|
var preSignedURL = await FromServiceAccountData(stream).SignAsync(_bucket, MakePath(domain, path), expire, HttpMethod.Get);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
return MakeUri(preSignedURL);
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
public Uri GetUriShared(string domain, string path)
|
|
|
|
{
|
2022-03-25 16:26:06 +00:00
|
|
|
return new Uri(SecureHelper.IsSecure(_httpContextAccessor.HttpContext, _options) ? _bucketSSlRoot : _bucketRoot, MakePath(domain, path));
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2022-04-15 09:08:06 +00:00
|
|
|
public override Task<Stream> GetReadStreamAsync(string domain, string path)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
return GetReadStreamAsync(domain, path, 0);
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-04-15 09:08:06 +00:00
|
|
|
public override async Task<Stream> GetReadStreamAsync(string domain, string path, int offset)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-03-25 16:26:06 +00:00
|
|
|
var tempStream = _tempStream.Create();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
await storage.DownloadObjectAsync(_bucket, MakePath(domain, path), tempStream);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (offset > 0)
|
2022-03-17 15:01:39 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
tempStream.Seek(offset, SeekOrigin.Begin);
|
2022-03-17 15:01:39 +00:00
|
|
|
}
|
2021-05-21 13:26:42 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
tempStream.Position = 0;
|
2021-05-21 13:26:42 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
return tempStream;
|
|
|
|
}
|
2021-05-21 13:26:42 +00:00
|
|
|
|
2022-04-15 09:08:06 +00:00
|
|
|
public override Task<Uri> SaveAsync(string domain, string path, Stream stream)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
return SaveAsync(domain, path, stream, string.Empty, string.Empty);
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2021-05-21 13:26:42 +00:00
|
|
|
|
2022-04-15 09:08:06 +00:00
|
|
|
public override Task<Uri> SaveAsync(string domain, string path, Stream stream, ACL acl)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
return SaveAsync(domain, path, stream, null, null, acl);
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2021-05-21 13:26:42 +00:00
|
|
|
|
2022-04-15 09:08:06 +00:00
|
|
|
public override Task<Uri> SaveAsync(string domain, string path, Stream stream, string contentType, string contentDisposition)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
return SaveAsync(domain, path, stream, contentType, contentDisposition, ACL.Auto);
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2021-05-21 13:26:42 +00:00
|
|
|
|
2022-04-15 09:08:06 +00:00
|
|
|
public override Task<Uri> SaveAsync(string domain, string path, Stream stream, string contentEncoding, int cacheDays)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
return SaveAsync(domain, path, stream, string.Empty, string.Empty, ACL.Auto, contentEncoding, cacheDays);
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-06-30 13:13:31 +00:00
|
|
|
private bool EnableQuotaCheck(string domain)
|
|
|
|
{
|
|
|
|
return (QuotaController != null) && !domain.EndsWith("_temp");
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public async Task<Uri> SaveAsync(string domain, string path, Stream stream, string contentType,
|
|
|
|
string contentDisposition, ACL acl, string contentEncoding = null, int cacheDays = 5)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-03-25 16:26:06 +00:00
|
|
|
var buffered = _tempStream.GetBuffered(stream);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-06-30 13:13:31 +00:00
|
|
|
if (EnableQuotaCheck(domain))
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
QuotaController.QuotaUsedCheck(buffered.Length);
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var mime = string.IsNullOrEmpty(contentType)
|
|
|
|
? MimeMapping.GetMimeMapping(Path.GetFileName(path))
|
|
|
|
: contentType;
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var uploadObjectOptions = new UploadObjectOptions
|
|
|
|
{
|
|
|
|
PredefinedAcl = acl == ACL.Auto ? GetDomainACL(domain) : GetGoogleCloudAcl(acl)
|
|
|
|
};
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
buffered.Position = 0;
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
var uploaded = await storage.UploadObjectAsync(_bucket, MakePath(domain, path), mime, buffered, uploadObjectOptions);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
uploaded.ContentEncoding = contentEncoding;
|
|
|
|
uploaded.CacheControl = string.Format("public, maxage={0}", (int)TimeSpan.FromDays(cacheDays).TotalSeconds);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (uploaded.Metadata == null)
|
|
|
|
{
|
|
|
|
uploaded.Metadata = new Dictionary<string, string>();
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
uploaded.Metadata["Expires"] = DateTime.UtcNow.Add(TimeSpan.FromDays(cacheDays)).ToString("R");
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (!string.IsNullOrEmpty(contentDisposition))
|
|
|
|
{
|
|
|
|
uploaded.ContentDisposition = contentDisposition;
|
|
|
|
}
|
|
|
|
else if (mime == "application/octet-stream")
|
|
|
|
{
|
|
|
|
uploaded.ContentDisposition = "attachment";
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
storage.UpdateObject(uploaded);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
// InvalidateCloudFront(MakePath(domain, path));
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
QuotaUsedAdd(domain, buffered.Length);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
return await GetUriAsync(domain, path);
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task DeleteAsync(string domain, string path)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var key = MakePath(domain, path);
|
2022-02-23 19:42:34 +00:00
|
|
|
var size = await GetFileSizeAsync(domain, path);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await storage.DeleteObjectAsync(_bucket, key);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
QuotaUsedDelete(domain, size);
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task DeleteFilesAsync(string domain, string folderPath, string pattern, bool recursive)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
IAsyncEnumerable<Google.Apis.Storage.v1.Data.Object> objToDel;
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (recursive)
|
|
|
|
{
|
|
|
|
objToDel = storage
|
2022-02-23 19:42:34 +00:00
|
|
|
.ListObjectsAsync(_bucket, MakePath(domain, folderPath))
|
|
|
|
.Where(x => Wildcard.IsMatch(pattern, Path.GetFileName(x.Name)));
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
2022-02-10 11:24:16 +00:00
|
|
|
else
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
objToDel = AsyncEnumerable.Empty<Google.Apis.Storage.v1.Data.Object>();
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await foreach (var obj in objToDel)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
await storage.DeleteObjectAsync(_bucket, obj.Name);
|
2022-02-10 11:24:16 +00:00
|
|
|
QuotaUsedDelete(domain, Convert.ToInt64(obj.Size));
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override Task DeleteFilesAsync(string domain, List<string> paths)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-03-17 15:01:39 +00:00
|
|
|
if (paths.Count == 0)
|
|
|
|
{
|
|
|
|
return Task.CompletedTask;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
return InternalDeleteFilesAsync(domain, paths);
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
private async Task InternalDeleteFilesAsync(string domain, List<string> paths)
|
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
var keysToDel = new List<string>();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
long quotaUsed = 0;
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
foreach (var path in paths)
|
|
|
|
{
|
|
|
|
try
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var key = MakePath(domain, path);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (QuotaController != null)
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
quotaUsed += await GetFileSizeAsync(domain, path);
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
keysToDel.Add(key);
|
2022-02-10 11:06:37 +00:00
|
|
|
}
|
2022-02-10 11:24:16 +00:00
|
|
|
catch (FileNotFoundException)
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-17 15:01:39 +00:00
|
|
|
if (keysToDel.Count == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
foreach (var e in keysToDel)
|
|
|
|
{
|
|
|
|
await storage.DeleteObjectAsync(_bucket, e);
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (quotaUsed > 0)
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
QuotaUsedDelete(domain, quotaUsed);
|
|
|
|
}
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task DeleteFilesAsync(string domain, string folderPath, DateTime fromDate, DateTime toDate)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
var objToDel = GetObjectsAsync(domain, folderPath, true)
|
|
|
|
.Where(x => x.Updated >= fromDate && x.Updated <= toDate);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await foreach (var obj in objToDel)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
await storage.DeleteObjectAsync(_bucket, obj.Name);
|
2022-02-10 11:24:16 +00:00
|
|
|
QuotaUsedDelete(domain, Convert.ToInt64(obj.Size));
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task MoveDirectoryAsync(string srcdomain, string srcdir, string newdomain, string newdir)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
|
|
|
var srckey = MakePath(srcdomain, srcdir);
|
|
|
|
var dstkey = MakePath(newdomain, newdir);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var objects = storage.ListObjects(_bucket, srckey);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
foreach (var obj in objects)
|
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
await storage.CopyObjectAsync(_bucket, srckey, _bucket, dstkey, new CopyObjectOptions
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
|
|
|
DestinationPredefinedAcl = GetDomainACL(newdomain)
|
|
|
|
});
|
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await storage.DeleteObjectAsync(_bucket, srckey);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
|
|
|
}
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task<Uri> MoveAsync(string srcdomain, string srcpath, string newdomain, string newpath, bool quotaCheckFileSize = true)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var srcKey = MakePath(srcdomain, srcpath);
|
|
|
|
var dstKey = MakePath(newdomain, newpath);
|
2022-02-23 19:42:34 +00:00
|
|
|
var size = await GetFileSizeAsync(srcdomain, srcpath);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
storage.CopyObject(_bucket, srcKey, _bucket, dstKey, new CopyObjectOptions
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
DestinationPredefinedAcl = GetDomainACL(newdomain)
|
|
|
|
});
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await DeleteAsync(srcdomain, srcpath);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
QuotaUsedDelete(srcdomain, size);
|
|
|
|
QuotaUsedAdd(newdomain, size, quotaCheckFileSize);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
return await GetUriAsync(newdomain, newpath);
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-04-15 09:08:06 +00:00
|
|
|
public override Task<Uri> SaveTempAsync(string domain, out string assignedPath, Stream stream)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
assignedPath = Guid.NewGuid().ToString();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
return SaveAsync(domain, assignedPath, stream);
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override IAsyncEnumerable<string> ListDirectoriesRelativeAsync(string domain, string path, bool recursive)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
return GetObjectsAsync(domain, path, recursive)
|
|
|
|
.Select(x => x.Name.Substring(MakePath(domain, path + "/").Length));
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
private IEnumerable<Google.Apis.Storage.v1.Data.Object> GetObjects(string domain, string path, bool recursive)
|
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var items = storage.ListObjects(_bucket, MakePath(domain, path));
|
2021-05-21 13:26:42 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (recursive)
|
2021-05-21 13:26:42 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
return items;
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
return items.Where(x => x.Name.IndexOf('/', MakePath(domain, path + "/").Length) == -1);
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
private IAsyncEnumerable<Google.Apis.Storage.v1.Data.Object> GetObjectsAsync(string domain, string path, bool recursive)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
var items = storage.ListObjectsAsync(_bucket, MakePath(domain, path));
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-03-17 15:01:39 +00:00
|
|
|
if (recursive)
|
|
|
|
{
|
|
|
|
return items;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
return items.Where(x => x.Name.IndexOf('/', MakePath(domain, path + "/").Length) == -1);
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override IAsyncEnumerable<string> ListFilesRelativeAsync(string domain, string path, string pattern, bool recursive)
|
|
|
|
{
|
|
|
|
return GetObjectsAsync(domain, path, recursive).Where(x => Wildcard.IsMatch(pattern, Path.GetFileName(x.Name)))
|
|
|
|
.Select(x => x.Name.Substring(MakePath(domain, path + "/").Length).TrimStart('/'));
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
public override async Task<bool> IsFileAsync(string domain, string path)
|
|
|
|
{
|
|
|
|
var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var objects = await storage.ListObjectsAsync(_bucket, MakePath(domain, path)).ReadPageAsync(1);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
return objects.Any();
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override Task<bool> IsDirectoryAsync(string domain, string path)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
return IsFileAsync(domain, path);
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task DeleteDirectoryAsync(string domain, string path)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var objToDel = storage
|
2022-02-23 19:42:34 +00:00
|
|
|
.ListObjectsAsync(_bucket, MakePath(domain, path));
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await foreach (var obj in objToDel)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
await storage.DeleteObjectAsync(_bucket, obj.Name);
|
2022-02-10 11:24:16 +00:00
|
|
|
QuotaUsedDelete(domain, Convert.ToInt64(obj.Size));
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task<long> GetFileSizeAsync(string domain, string path)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
var obj = await storage.GetObjectAsync(_bucket, MakePath(domain, path));
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
return obj.Size.HasValue ? Convert.ToInt64(obj.Size.Value) : 0;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task<long> GetDirectorySizeAsync(string domain, string path)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var objToDel = storage
|
2022-01-25 09:29:11 +00:00
|
|
|
.ListObjectsAsync(_bucket, MakePath(domain, path));
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
long result = 0;
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await foreach (var obj in objToDel)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
if (obj.Size.HasValue)
|
|
|
|
{
|
|
|
|
result += Convert.ToInt64(obj.Size.Value);
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task<long> ResetQuotaAsync(string domain)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var objects = storage
|
2022-01-25 09:29:11 +00:00
|
|
|
.ListObjectsAsync(_bucket, MakePath(domain, string.Empty));
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (QuotaController != null)
|
|
|
|
{
|
|
|
|
long size = 0;
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await foreach (var obj in objects)
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
|
|
|
if (obj.Size.HasValue)
|
2022-02-10 11:06:37 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
size += Convert.ToInt64(obj.Size.Value);
|
2022-02-10 11:06:37 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
|
|
|
|
2022-02-11 10:04:06 +00:00
|
|
|
QuotaController.QuotaUsedSet(Modulename, domain, DataList.GetData(domain), size);
|
2022-02-10 11:24:16 +00:00
|
|
|
|
|
|
|
return size;
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-01-25 09:29:11 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task<long> GetUsedQuotaAsync(string domain)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var objects = storage
|
2022-01-25 09:29:11 +00:00
|
|
|
.ListObjectsAsync(_bucket, MakePath(domain, string.Empty));
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
long result = 0;
|
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await foreach (var obj in objects)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
if (obj.Size.HasValue)
|
2019-08-15 13:35:18 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
result += Convert.ToInt64(obj.Size.Value);
|
|
|
|
}
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
return result;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task<Uri> CopyAsync(string srcdomain, string srcpath, string newdomain, string newpath)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
var size = await GetFileSizeAsync(srcdomain, srcpath);
|
2022-02-10 11:24:16 +00:00
|
|
|
|
|
|
|
var options = new CopyObjectOptions
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
DestinationPredefinedAcl = GetDomainACL(newdomain)
|
|
|
|
};
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await storage.CopyObjectAsync(_bucket, MakePath(srcdomain, srcpath), _bucket, MakePath(newdomain, newpath), options);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
QuotaUsedAdd(newdomain, size);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
return await GetUriAsync(newdomain, newpath);
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task CopyDirectoryAsync(string srcdomain, string srcdir, string newdomain, string newdir)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
var srckey = MakePath(srcdomain, srcdir);
|
|
|
|
var dstkey = MakePath(newdomain, newdir);
|
|
|
|
//List files from src
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
using var storage = GetStorage();
|
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
var objects = storage.ListObjectsAsync(_bucket, srckey);
|
2022-02-10 11:24:16 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await foreach (var obj in objects)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
await storage.CopyObjectAsync(_bucket, srckey, _bucket, dstkey, new CopyObjectOptions
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
DestinationPredefinedAcl = GetDomainACL(newdomain)
|
|
|
|
});
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
QuotaUsedAdd(newdomain, Convert.ToInt64(obj.Size));
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 09:08:06 +00:00
|
|
|
public override async Task<string> SavePrivateAsync(string domain, string path, Stream stream, DateTime expires)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
|
|
|
|
2022-03-25 16:26:06 +00:00
|
|
|
var buffered = _tempStream.GetBuffered(stream);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var uploadObjectOptions = new UploadObjectOptions
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
PredefinedAcl = PredefinedObjectAcl.BucketOwnerFullControl
|
|
|
|
};
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
buffered.Position = 0;
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
var uploaded = await storage.UploadObjectAsync(_bucket, MakePath(domain, path), "application/octet-stream", buffered, uploadObjectOptions);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
uploaded.CacheControl = string.Format("public, maxage={0}", (int)TimeSpan.FromDays(5).TotalSeconds);
|
|
|
|
uploaded.ContentDisposition = "attachment";
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (uploaded.Metadata == null)
|
|
|
|
{
|
|
|
|
uploaded.Metadata = new Dictionary<string, string>();
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
uploaded.Metadata["Expires"] = DateTime.UtcNow.Add(TimeSpan.FromDays(5)).ToString("R");
|
|
|
|
uploaded.Metadata.Add("private-expire", expires.ToFileTimeUtc().ToString(CultureInfo.InvariantCulture));
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await storage.UpdateObjectAsync(uploaded);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
using var mStream = new MemoryStream(Encoding.UTF8.GetBytes(_json ?? ""));
|
2022-06-02 16:24:28 +00:00
|
|
|
var signDuration = expires.Date == DateTime.MinValue ? expires.TimeOfDay : expires.Subtract(DateTime.UtcNow);
|
|
|
|
var preSignedURL = await FromServiceAccountData(mStream)
|
|
|
|
.SignAsync(RequestTemplate.FromBucket(_bucket).WithObjectName(MakePath(domain, path)), Options.FromDuration(signDuration));
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
//TODO: CNAME!
|
|
|
|
return preSignedURL;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task DeleteExpiredAsync(string domain, string path, TimeSpan oldThreshold)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
var objects = storage.ListObjectsAsync(_bucket, MakePath(domain, path));
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await foreach (var obj in objects)
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
var objInfo = await storage.GetObjectAsync(_bucket, MakePath(domain, path), null);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var privateExpireKey = objInfo.Metadata["private-expire"];
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (string.IsNullOrEmpty(privateExpireKey))
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
continue;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (!long.TryParse(privateExpireKey, out var fileTime))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (DateTime.UtcNow <= DateTime.FromFileTimeUtc(fileTime))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2022-02-10 11:06:37 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
await storage.DeleteObjectAsync(_bucket, MakePath(domain, path));
|
2022-02-10 11:06:37 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
#region chunking
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task<string> InitiateChunkedUploadAsync(string domain, string path)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
using var storage = GetStorage();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var tempUploader = storage.CreateObjectUploader(_bucket, MakePath(domain, path), null, new MemoryStream());
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
var sessionUri = await tempUploader.InitiateSessionAsync();
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
return sessionUri.ToString();
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-01-25 09:29:11 +00:00
|
|
|
|
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task<string> UploadChunkAsync(string domain,
|
|
|
|
string path,
|
|
|
|
string uploadUri,
|
|
|
|
Stream stream,
|
|
|
|
long defaultChunkSize,
|
|
|
|
int chunkNumber,
|
|
|
|
long chunkLength)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var bytesRangeStart = Convert.ToString((chunkNumber - 1) * defaultChunkSize);
|
|
|
|
var bytesRangeEnd = Convert.ToString((chunkNumber - 1) * defaultChunkSize + chunkLength - 1);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
var totalBytes = "*";
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (chunkLength != defaultChunkSize)
|
|
|
|
{
|
|
|
|
totalBytes = Convert.ToString((chunkNumber - 1) * defaultChunkSize + chunkLength);
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-04-14 19:23:57 +00:00
|
|
|
var request = new HttpRequestMessage
|
|
|
|
{
|
|
|
|
RequestUri = new Uri(uploadUri),
|
|
|
|
Method = HttpMethod.Put
|
|
|
|
};
|
2022-02-10 11:24:16 +00:00
|
|
|
request.Content = new StreamContent(stream);
|
2022-06-30 13:13:31 +00:00
|
|
|
request.Content.Headers.ContentRange = new ContentRangeHeaderValue(Convert.ToInt64(bytesRangeStart),
|
|
|
|
Convert.ToInt64(bytesRangeEnd),
|
|
|
|
Convert.ToInt64(totalBytes));
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-16 09:47:11 +00:00
|
|
|
const int MAX_RETRIES = 100;
|
2022-02-10 11:24:16 +00:00
|
|
|
int millisecondsTimeout;
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
for (var i = 0; i < MAX_RETRIES; i++)
|
|
|
|
{
|
2022-02-16 09:47:11 +00:00
|
|
|
millisecondsTimeout = Math.Min(Convert.ToInt32(Math.Pow(2, i)) + RandomNumberGenerator.GetInt32(1000), 32 * 1000);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
try
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-03-25 16:26:06 +00:00
|
|
|
var httpClient = _clientFactory.CreateClient();
|
2022-02-23 19:42:34 +00:00
|
|
|
using var response = await httpClient.SendAsync(request);
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
catch (HttpRequestException ex)
|
|
|
|
{
|
|
|
|
var status = (int)ex.StatusCode;
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (status == 408 || status == 500 || status == 502 || status == 503 || status == 504)
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
Thread.Sleep(millisecondsTimeout);
|
|
|
|
continue;
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (status != 308)
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2020-11-17 10:47:17 +00:00
|
|
|
throw;
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
catch
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
await AbortChunkedUploadAsync(domain, path, uploadUri);
|
2022-02-10 11:24:16 +00:00
|
|
|
throw;
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
return string.Empty;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override async Task<Uri> FinalizeChunkedUploadAsync(string domain, string path, string uploadUri, Dictionary<int, string> eTags)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
|
|
|
if (QuotaController != null)
|
2019-06-04 14:43:20 +00:00
|
|
|
{
|
2022-02-23 19:42:34 +00:00
|
|
|
var size = await GetFileSizeAsync(domain, path);
|
2022-02-10 11:24:16 +00:00
|
|
|
QuotaUsedAdd(domain, size);
|
2019-06-04 14:43:20 +00:00
|
|
|
}
|
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
return await GetUriAsync(domain, path);
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
public override Task AbortChunkedUploadAsync(string domain, string path, string uploadUri)
|
|
|
|
{
|
|
|
|
return Task.CompletedTask;
|
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
#endregion
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
public override string GetUploadForm(string domain, string directoryPath, string redirectTo, long maxUploadSize, string contentType, string contentDisposition, string submitLabel)
|
|
|
|
{
|
|
|
|
throw new NotImplementedException();
|
|
|
|
}
|
2022-02-10 10:43:28 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
public override string GetUploadUrl()
|
|
|
|
{
|
|
|
|
throw new NotImplementedException();
|
|
|
|
}
|
2022-02-10 10:43:28 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
public override string GetPostParams(string domain, string directoryPath, long maxUploadSize, string contentType, string contentDisposition)
|
|
|
|
{
|
|
|
|
throw new NotImplementedException();
|
|
|
|
}
|
2022-02-10 10:43:28 +00:00
|
|
|
|
2022-04-15 09:08:06 +00:00
|
|
|
protected override Task<Uri> SaveWithAutoAttachmentAsync(string domain, string path, Stream stream, string attachmentFileName)
|
2022-02-10 11:24:16 +00:00
|
|
|
{
|
2022-02-16 09:47:11 +00:00
|
|
|
var contentDisposition = $"attachment; filename={HttpUtility.UrlPathEncode(attachmentFileName)};";
|
2022-02-10 11:24:16 +00:00
|
|
|
if (attachmentFileName.Any(c => c >= 0 && c <= 127))
|
2022-02-10 10:43:28 +00:00
|
|
|
{
|
2022-02-16 09:47:11 +00:00
|
|
|
contentDisposition = $"attachment; filename*=utf-8''{HttpUtility.UrlPathEncode(attachmentFileName)};";
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2022-02-23 19:42:34 +00:00
|
|
|
return SaveAsync(domain, path, stream, null, contentDisposition);
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|
2022-02-10 10:43:28 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
private StorageClient GetStorage()
|
|
|
|
{
|
|
|
|
var credential = GoogleCredential.FromJson(_json);
|
|
|
|
|
|
|
|
return StorageClient.Create(credential);
|
|
|
|
}
|
2022-02-10 10:43:28 +00:00
|
|
|
|
2022-02-23 19:42:34 +00:00
|
|
|
private Task<StorageClient> GetStorageAsync()
|
|
|
|
{
|
|
|
|
var credential = GoogleCredential.FromJson(_json);
|
|
|
|
|
|
|
|
return StorageClient.CreateAsync(credential);
|
|
|
|
}
|
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
private string MakePath(string domain, string path)
|
|
|
|
{
|
|
|
|
string result;
|
|
|
|
|
|
|
|
path = path.TrimStart('\\', '/').TrimEnd('/').Replace('\\', '/');
|
2022-02-10 10:43:28 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
if (!string.IsNullOrEmpty(_subDir))
|
|
|
|
{
|
|
|
|
if (_subDir.Length == 1 && (_subDir[0] == '/' || _subDir[0] == '\\'))
|
2022-03-17 15:01:39 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
result = path;
|
2022-03-17 15:01:39 +00:00
|
|
|
}
|
2022-02-10 11:24:16 +00:00
|
|
|
else
|
2022-03-17 15:01:39 +00:00
|
|
|
{
|
2022-02-16 09:47:11 +00:00
|
|
|
result = $"{_subDir}/{path}"; // Ignory all, if _subDir is not null
|
2022-03-17 15:01:39 +00:00
|
|
|
}
|
2022-02-10 10:43:28 +00:00
|
|
|
}
|
2022-02-10 11:24:16 +00:00
|
|
|
else//Key combined from module+domain+filename
|
2022-03-17 15:01:39 +00:00
|
|
|
{
|
2022-02-16 09:47:11 +00:00
|
|
|
result = $"{Tenant}/{Modulename}/{domain}/{path}";
|
2022-03-17 15:01:39 +00:00
|
|
|
}
|
2022-02-10 10:43:28 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
result = result.Replace("//", "/").TrimStart('/');
|
|
|
|
if (_lowerCasing)
|
2022-02-10 10:43:28 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
result = result.ToLowerInvariant();
|
2022-02-10 10:43:28 +00:00
|
|
|
}
|
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private Uri MakeUri(string preSignedURL)
|
|
|
|
{
|
|
|
|
var uri = new Uri(preSignedURL);
|
|
|
|
var signedPart = uri.PathAndQuery.TrimStart('/');
|
|
|
|
|
|
|
|
return new Uri(uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) ? _bucketSSlRoot : _bucketRoot, signedPart);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void InvalidateCloudFront(params string[] paths)
|
|
|
|
{
|
|
|
|
throw new NotImplementedException();
|
|
|
|
}
|
|
|
|
|
|
|
|
private PredefinedObjectAcl GetGoogleCloudAcl(ACL acl)
|
|
|
|
{
|
|
|
|
return PredefinedObjectAcl.PublicRead;
|
|
|
|
//return acl switch
|
|
|
|
//{
|
|
|
|
// ACL.Read => PredefinedObjectAcl.PublicRead,
|
|
|
|
// _ => PredefinedObjectAcl.PublicRead,
|
|
|
|
//};
|
|
|
|
}
|
|
|
|
|
|
|
|
private PredefinedObjectAcl GetDomainACL(string domain)
|
|
|
|
{
|
|
|
|
if (GetExpire(domain) != TimeSpan.Zero)
|
2022-02-10 10:43:28 +00:00
|
|
|
{
|
2022-02-10 11:24:16 +00:00
|
|
|
return PredefinedObjectAcl.Private;
|
2022-02-10 10:43:28 +00:00
|
|
|
}
|
|
|
|
|
2022-02-16 09:47:11 +00:00
|
|
|
if (_domainsAcl.TryGetValue(domain, out var value))
|
2022-02-10 10:43:28 +00:00
|
|
|
{
|
2022-02-16 09:47:11 +00:00
|
|
|
return value;
|
2022-02-10 10:43:28 +00:00
|
|
|
}
|
2022-02-10 11:24:16 +00:00
|
|
|
return _moduleAcl;
|
2022-02-10 10:43:28 +00:00
|
|
|
}
|
2019-06-04 14:43:20 +00:00
|
|
|
|
2022-02-10 11:24:16 +00:00
|
|
|
}
|