/// <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> /// Releases all unmanaged and optionally managed resources. /// </summary> /// <param name="disposing">When true, managed resources are released.</param> protected override void Dispose(bool disposing) { if (this.disposed) { return; } if (disposing) { if (this.fsStream != null) { this.fsStream.Dispose(); } else { try { this.Flush(); if (this.fileAccess != CMS.IO.FileAccess.Read && this.fileMode != CMS.IO.FileMode.Open && this.fsTemp.CanWrite && !this.mMultiPartUploadMode) { this.fsTemp.Seek(0L, SeekOrigin.Begin); this.Provider.PutDataFromStreamToObject(this.obj, this.fsTemp); this.SetLastWriteTimeAndCreationTimeToS3Object(); } } catch (Exception ex) { EventLogProvider.LogException("Custom Amazon", "STREAMDISPOSE", ex, 0); } finally { this.fsTemp.Dispose(); if (this.mReadSize > 0) { FileDebug.LogReadEnd(this.mReadSize); this.mReadSize = -1; } this.LogFileOperation(this.mPath, "Close", -1); } } } this.disposed = true; base.Dispose(disposing); }
/// <summary>Closes current stream.</summary> public override void Close() { if (this.fsStream != null) { this.fsStream.Close(); } else { this.Dispose(true); this.fsTemp.Close(); } if (this.mReadSize > 0) { FileDebug.LogReadEnd(this.mReadSize); this.mReadSize = -1; } this.LogFileOperation(this.mPath, nameof(Close), -1); }
/// <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> /// Returns an enumerable collection of file names that match a search pattern in a specified path. /// </summary> /// <param name="path">The relative or absolute path to the directory to search. This string is not case-sensitive.</param> /// <param name="searchPattern">Search pattern.</param> /// <returns>An enumerable collection of the full names (including paths) for the files in the directory specified by <paramref name="path" /> and that match the specified search pattern.</returns> public override IEnumerable <string> EnumerateFiles(string path, string searchPattern) { FileDebug.LogFileOperation(path, nameof(EnumerateFiles), -1, null, null, "Custom Amazon"); return(this.EnumerateFilesCore(path, searchPattern)); }
/// <summary> /// Gets the names of the subdirectories (including their paths) that match the specified search pattern in the current directory, /// and optionally searches subdirectories. /// </summary> /// <param name="path">The relative or absolute path to the directory to search. This string is not case-sensitive.</param> /// <param name="searchPattern">Search pattern.</param> /// <param name="searchOption">One of the enumeration values that specifies whether the search operation should include all subdirectories or only the current directory.</param> /// <returns>An array of the full names (including paths) of the subdirectories that match the specified criteria, or an empty array if no directories are found.</returns> public override string[] GetDirectories(string path, string searchPattern, CMS.IO.SearchOption searchOption) { string[] array = this.EnumerateDirectoriesCore(path, searchPattern, searchOption).ToArray(); FileDebug.LogFileOperation(path, nameof(GetDirectories), -1, array.Length.ToString(), null, "Custom Amazon"); return(array); }