/// <summary>Remove S3 file from cache local storage.</summary>
        /// <param name="obj">S3 object to be removed from cache local storage</param>
        private static void RemoveFromCache(IS3ObjectInfo obj)
        {
            string path = CMS.IO.Path.Combine(PathHelper.CachePath, PathHelper.GetPathFromObjectKey(obj.Key, false));

            DeleteFileFromLocalPath(path);
            DeleteFileFromLocalPath(path + ".etag");
        }
 /// <summary>Appends text to Amazon S3 storage object.</summary>
 /// <param name="obj">Object info.</param>
 /// <param name="content">Content to append.</param>
 public void AppendTextToObject(IS3ObjectInfo obj, string content)
 {
     if (this.ObjectExists(obj))
     {
         if (obj.IsLocked)
         {
             throw new Exception($"[IS3ObjectInfoProvider.AppendTextToObject]: Couldn't upload object {obj.Key} because it is used by another process.");
         }
         obj.Lock();
         System.IO.Stream objectContent = this.GetObjectContent(obj, System.IO.FileMode.Open,
                                                                System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite, 4096);
         string str = null;
         using (CMS.IO.StreamReader streamReader = CMS.IO.StreamReader.New(objectContent))
         {
             str = streamReader.ReadToEnd();
         }
         PutObjectRequest putRequest = CreatePutRequest(obj.Key, obj.BucketName);
         putRequest.ContentBody = str + content;
         PutObjectResponse response = this.S3Client.PutObject(putRequest);
         this.SetS3ObjectMetadaFromResponse(obj, response, 0L);
         FileDebug.LogFileOperation(PathHelper.GetPathFromObjectKey(obj.Key, true), nameof(AppendTextToObject), "Custom Amazon");
         obj.UnLock();
         RemoveRequestCache(obj.Key);
     }
     else
     {
         this.PutTextToObject(obj, content);
     }
 }
        /// <summary>Puts data from stream to Amazon S3 storage.</summary>
        /// <param name="obj">Object info.</param>
        /// <param name="stream">Stream to upload.</param>
        public void PutDataFromStreamToObject(IS3ObjectInfo obj, System.IO.Stream stream)
        {
            if (obj.IsLocked)
            {
                throw new Exception($"[IS3ObjectInfoProvider.PutDataFromStreamToObject]: Couldn't upload object {obj.Key} because it is used by another process.");
            }
            obj.Lock();
            string bucketName = obj.BucketName;
            long   length     = stream.Length;

            if (length > RECOMMENDED_SIZE_FOR_MULTIPART_UPLOAD)
            {
                CompleteMultipartUploadResponse response = this.MultiPartUploader.UploadFromStream(obj.Key, bucketName, stream);
                this.SetS3ObjectMetadaFromResponse(obj, response, length);
            }
            else
            {
                PutObjectRequest putRequest = CreatePutRequest(obj.Key, bucketName);
                putRequest.InputStream = stream;
                PutObjectResponse response = this.S3Client.PutObject(putRequest);
                this.SetS3ObjectMetadaFromResponse(obj, response, length);
            }
            FileDebug.LogFileOperation(PathHelper.GetPathFromObjectKey(obj.Key, true), "PutStreamToObject", "Custom Amazon");
            obj.UnLock();
            RemoveRequestCache(obj.Key);
        }
 /// <summary>Unlocks current object.</summary>
 public void UnLock()
 {
     if (!this.Provider.ObjectExists(this))
     {
         return;
     }
     FileDebug.LogFileOperation(PathHelper.GetPathFromObjectKey(this.Key, true), "Unlock", "Custom Amazon");
     this.SetMetadata(S3ObjectInfoProvider.LOCK, "False", true, false);
 }
 /// <summary>Locks current object.</summary>
 public void Lock()
 {
     if (!this.Provider.ObjectExists(this))
     {
         return;
     }
     FileDebug.LogFileOperation(PathHelper.GetPathFromObjectKey(this.Key, true), nameof(Lock), "Custom Amazon");
     this.SetMetadata(nameof(Lock), "True", true, false);
 }
        /// <summary>Creates empty object.</summary>
        /// <param name="obj">Object info.</param>
        public void CreateEmptyObject(IS3ObjectInfo obj)
        {
            string           pathFromObjectKey = PathHelper.GetPathFromObjectKey(obj.Key, true);
            PutObjectRequest putRequest        = CreatePutRequest(obj.Key, GetBucketName(pathFromObjectKey));

            putRequest.InputStream = new System.IO.MemoryStream();
            PutObjectResponse response = this.S3Client.PutObject(putRequest);

            this.SetS3ObjectMetadaFromResponse(obj, response, 0L);
            FileDebug.LogFileOperation(PathHelper.GetPathFromObjectKey(obj.Key, true), nameof(CreateEmptyObject), "Custom Amazon");
            RemoveRequestCache(obj.Key);
        }
        /// <summary>Returns object content as a stream.</summary>
        /// <param name="obj">Object info.</param>
        /// <param name="fileMode">File mode.</param>
        /// <param name="fileAccess">File access.</param>
        /// <param name="fileShare">Sharing permissions.</param>
        /// <param name="bufferSize">Buffer size.</param>
        public System.IO.Stream GetObjectContent(IS3ObjectInfo obj, System.IO.FileMode fileMode = System.IO.FileMode.Open,
                                                 System.IO.FileAccess fileAccess = System.IO.FileAccess.Read, System.IO.FileShare fileShare = System.IO.FileShare.ReadWrite, int bufferSize = 4096)
        {
            if (!this.ObjectExists(obj))
            {
                return(null);
            }
            AutoResetEvent orAdd = mS3ObjectEvents.GetOrAdd(obj.Key, new AutoResetEvent(true));

            try
            {
                string tempPath = CMS.IO.Path.Combine(PathHelper.TempPath, PathHelper.GetPathFromObjectKey(obj.Key, false));
                Directory.CreateDiskDirectoryStructure(tempPath);
                string cachePath = CMS.IO.Path.Combine(PathHelper.CachePath, PathHelper.GetPathFromObjectKey(obj.Key, false));
                string path      = $"{cachePath}.etag";
                orAdd.WaitOne();
                if (CMS.IO.File.Exists(cachePath) && System.IO.File.ReadAllText(path).Trim() == obj.ETag)
                {
                    orAdd.Set();
                    System.IO.FileStream fileStream = new System.IO.FileStream(cachePath, fileMode, fileAccess, fileShare, bufferSize);
                    FileDebug.LogFileOperation(PathHelper.GetPathFromObjectKey(obj.Key, true), "GetObjectFromCache", "Custom Amazon");
                    return(fileStream);
                }
                using (GetObjectResponse getObjectResponse = this.S3Client.GetObject(new GetObjectRequest()
                {
                    BucketName = obj.BucketName,
                    Key = obj.Key
                }))
                {
                    getObjectResponse.WriteResponseStreamToFile(tempPath);
                }
                FileDebug.LogFileOperation(PathHelper.GetPathFromObjectKey(obj.Key, true), "GetObjectFromS3Storage", "Custom Amazon");
                Directory.CreateDiskDirectoryStructure(cachePath);
                System.IO.File.Copy(tempPath, cachePath, true);
                System.IO.File.WriteAllText(path, obj.ETag);
                if (fileMode == System.IO.FileMode.Append && fileAccess != System.IO.FileAccess.Read)
                {
                    fileMode   = System.IO.FileMode.Open;
                    fileAccess = System.IO.FileAccess.ReadWrite;
                }
                return(new System.IO.FileStream(tempPath, fileMode, fileAccess, fileShare, bufferSize));
            }
            finally
            {
                orAdd.Set();
                mS3ObjectEvents.TryRemove(obj.Key, out orAdd);
            }
        }
 /// <summary>Deletes object from Amazon S3 storage.</summary>
 /// <param name="obj">Object info.</param>
 public void DeleteObject(IS3ObjectInfo obj)
 {
     this.S3Client.DeleteObject(new DeleteObjectRequest()
     {
         BucketName = obj.BucketName,
         Key        = obj.Key
     });
     obj.DeleteMetadataFile();
     FileDebug.LogFileOperation(PathHelper.GetPathFromObjectKey(obj.Key, true), nameof(DeleteObject), "Custom Amazon");
     RemoveRequestCache(obj.Key);
     try
     {
         RemoveFromTemp(obj);
         RemoveFromCache(obj);
     }
     catch (IOException)
     {
     }
 }
        /// <summary>Puts text to Amazon S3 storage object.</summary>
        /// <param name="obj">Object info.</param>
        /// <param name="content">Content to add.</param>
        public void PutTextToObject(IS3ObjectInfo obj, string content)
        {
            if (obj.IsLocked)
            {
                throw new Exception($"[IS3ObjectInfoProvider.PutTextToObject]: Couldn't upload object {obj.Key} because it is used by another process.");
            }
            string pathFromObjectKey = PathHelper.GetPathFromObjectKey(obj.Key, true);

            obj.Lock();
            PutObjectRequest putRequest = CreatePutRequest(obj.Key, GetBucketName(pathFromObjectKey));

            putRequest.ContentBody = content;
            PutObjectResponse response = this.S3Client.PutObject(putRequest);

            this.SetS3ObjectMetadaFromResponse(obj, response, 0L);
            FileDebug.LogFileOperation(PathHelper.GetPathFromObjectKey(obj.Key, true), nameof(PutTextToObject), "Custom Amazon");
            obj.UnLock();
            RemoveRequestCache(obj.Key);
        }
 /// <summary>Sets meta data to object.</summary>
 /// <param name="key">MetaData key.</param>
 /// <param name="value">Metadata value.</param>
 /// <param name="update">Indicates whether data are updated in S3 storage.</param>
 /// <param name="log">Indicates whether is operation logged.</param>
 public void SetMetadata(string key, string value, bool update, bool log)
 {
     if (this.Metadata.ContainsKey(key))
     {
         this.Metadata[key] = value;
     }
     else
     {
         this.Metadata.Add(key, value);
     }
     if (update)
     {
         this.SaveMetadata(PathHelper.GetPathFromObjectKey(this.Key, true), this.Metadata);
     }
     if (!log)
     {
         return;
     }
     FileDebug.LogFileOperation(PathHelper.GetPathFromObjectKey(this.Key, true), nameof(SetMetadata), "Custom Amazon");
 }
        /// <summary>
        /// Downloads metadata from the cloud. It ensures that RequestStockHelper always contains the "Exists" key.
        /// </summary>
        private void FetchMetadata()
        {
            if (AbstractStockHelper <RequestStockHelper> .Contains(STORAGE_KEY, this.Key + "|Exists", false))
            {
                return;
            }
            GetObjectMetadataRequest request = new GetObjectMetadataRequest
            {
                BucketName = this.BucketName,
                Key        = this.Key
            };

            try
            {
                GetObjectMetadataResponse objectMetadata = this.S3Client.GetObjectMetadata(request);
                AbstractStockHelper <RequestStockHelper> .AddToStorage(STORAGE_KEY, this.Key + "|Length", objectMetadata.ContentLength, false);

                AbstractStockHelper <RequestStockHelper> .AddToStorage(STORAGE_KEY, this.Key + "|ETag", objectMetadata.ETag, false);

                AbstractStockHelper <RequestStockHelper> .AddToStorage(STORAGE_KEY, this.Key + "|Exists", true, false);

                this.Metadata = this.LoadMetadata(PathHelper.GetPathFromObjectKey(this.Key, true));
                if (!this.Metadata.ContainsKey(S3ObjectInfoProvider.LAST_WRITE_TIME))
                {
                    this.Metadata.Add(S3ObjectInfoProvider.LAST_WRITE_TIME, ValidationHelper.GetString(objectMetadata.LastModified, string.Empty, "en-us"));
                }
                FileDebug.LogFileOperation(PathHelper.GetPathFromObjectKey(this.Key, true), nameof(FetchMetadata), "Custom Amazon");
            }
            catch (AmazonS3Exception ex)
            {
                if (ex.StatusCode == HttpStatusCode.NotFound)
                {
                    AbstractStockHelper <RequestStockHelper> .AddToStorage(STORAGE_KEY, this.Key + "|Exists", false, false);
                }
                else
                {
                    throw;
                }
            }
        }
        /// <summary>Copies object to another.</summary>
        /// <param name="sourceObject">Source object info.</param>
        /// <param name="destObject">Destination object info.</param>
        public void CopyObjects(IS3ObjectInfo sourceObject, IS3ObjectInfo destObject)
        {
            string            pathFromObjectKey = PathHelper.GetPathFromObjectKey(destObject.Key, true);
            CopyObjectRequest request           = new CopyObjectRequest()
            {
                SourceBucket      = sourceObject.BucketName,
                DestinationBucket = GetBucketName(pathFromObjectKey),
                SourceKey         = sourceObject.Key,
                DestinationKey    = destObject.Key
            };

            if (this.IsPublicAccess(pathFromObjectKey))
            {
                request.CannedACL = S3CannedACL.PublicRead;
            }
            CopyObjectResponse copyObjectResponse = this.S3Client.CopyObject(request);

            destObject.ETag   = copyObjectResponse.ETag;
            destObject.Length = ValidationHelper.GetLong(copyObjectResponse.ContentLength, 0L);
            FileDebug.LogFileOperation(PathHelper.GetPathFromObjectKey(sourceObject.Key, true) + "|" + PathHelper.GetPathFromObjectKey(destObject.Key, true), nameof(CopyObjects), "Custom Amazon");
            RemoveRequestCache(destObject.Key);
        }
 /// <summary>Remove S3 file from temporary local storage.</summary>
 /// <param name="obj">S3 object to be removed from temporary local storage</param>
 private static void RemoveFromTemp(IS3ObjectInfo obj)
 {
     DeleteFileFromLocalPath(CMS.IO.Path.Combine(PathHelper.TempPath, PathHelper.GetPathFromObjectKey(obj.Key, false)));
 }
        /// <summary>
        /// Returns list with objects from given bucket and under given path.
        /// </summary>
        /// <param name="path">Path.</param>
        /// <param name="type">Specifies which objects are returned (files, directories, both).</param>
        /// <param name="useFlatListing">Whether flat listing is used (all files from all subdirectories all in the result).</param>
        /// <param name="lower">Specifies whether path should be lowered inside method.</param>
        /// <param name="useCache">Indicates if results should be primary taken from cache to get better performance</param>
        /// <remarks>
        /// In order to allow to distinguish between files and directories, directories are listed with a trailing backslash.
        /// </remarks>
        public List <string> GetObjectsList(string path, ObjectTypeEnum type, bool useFlatListing = false, bool lower = true, bool useCache = true)
        {
            string bucketName = GetBucketName(path);

            if (string.IsNullOrEmpty(bucketName))
            {
                return(null);
            }
            var request = new ListObjectsRequest
            {
                BucketName = bucketName
            };

            if (!string.IsNullOrEmpty(path))
            {
                request.Prefix = PathHelper.GetObjectKeyFromPath(path).TrimEnd('/') + "/";
            }
            if (!useFlatListing)
            {
                request.Delimiter = "/";
            }
            var    source1 = new HashSet <string>();
            string key     = $"{request.Prefix }|{type.ToString("F")}|{useFlatListing}|{lower}";

            if (useCache && AbstractStockHelper <RequestStockHelper> .Contains(STORAGE_KEY, key, false))
            {
                CMS.IO.Directory.LogDirectoryOperation(path, "GetObjectListFromCache", "Custom Amazon");
                var source2 = AbstractStockHelper <RequestStockHelper> .GetItem(STORAGE_KEY, key, false) as HashSet <string>;

                if (source2 == null)
                {
                    return(new List <string>());
                }
                return(source2.ToList());
            }
            ListObjectsResponse listObjectsResponse;

            do
            {
                listObjectsResponse = this.S3Client.ListObjects(request);
                if (type == ObjectTypeEnum.Directories && !useFlatListing)
                {
                    foreach (string commonPrefix in listObjectsResponse.CommonPrefixes)
                    {
                        source1.Add($"{path}\\{CMS.IO.Path.GetFileName(commonPrefix.TrimEnd('/'))}\\");
                    }
                }
                else
                {
                    bool isDirectory = type == ObjectTypeEnum.FilesAndDirectories || type == ObjectTypeEnum.Directories;
                    bool isFile      = type == ObjectTypeEnum.FilesAndDirectories || type == ObjectTypeEnum.Files;
                    foreach (S3Object s3Object in listObjectsResponse.S3Objects)
                    {
                        bool directory = s3Object.Key.EndsWith("/", StringComparison.Ordinal);
                        if (directory && isDirectory || !directory && isFile)
                        {
                            source1.Add(PathHelper.GetPathFromObjectKey(s3Object.Key, true, directory, lower));
                        }
                    }
                }
                if (listObjectsResponse.IsTruncated)
                {
                    request.Marker = listObjectsResponse.NextMarker;
                }
            }while (listObjectsResponse.IsTruncated);
            source1.Remove(PathHelper.GetPathFromObjectKey(request.Prefix, true, true, lower));
            CMS.IO.Directory.LogDirectoryOperation(path, "ListObjects", "Custom Amazon");
            AbstractStockHelper <RequestStockHelper> .AddToStorage(STORAGE_KEY, key, (object)source1, false);

            return(source1.ToList());
        }