-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathS3File.cs
More file actions
176 lines (151 loc) · 6.93 KB
/
S3File.cs
File metadata and controls
176 lines (151 loc) · 6.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
using System.Net;
using Amazon.S3;
using Amazon.S3.Model;
namespace Ramstack.FileSystem.Amazon;
/// <summary>
/// Represents an implementation of <see cref="VirtualFile"/> that maps a file to an object in Amazon S3.
/// </summary>
internal sealed class S3File : VirtualFile
{
private readonly AmazonS3FileSystem _fs;
private readonly string _key;
/// <inheritdoc />
public override IVirtualFileSystem FileSystem => _fs;
/// <summary>
/// Initializes a new instance of the <see cref="S3File"/> class.
/// </summary>
/// <param name="fileSystem">The file system associated with this file.</param>
/// <param name="path">The path to the file.</param>
public S3File(AmazonS3FileSystem fileSystem, string path) : base(path) =>
(_fs, _key) = (fileSystem, path[1..]);
/// <summary>
/// Initializes a new instance of the <see cref="VirtualFile"/> class with the specified path and properties.
/// </summary>
/// <param name="fileSystem">The file system associated with this file.</param>
/// <param name="path">The full path of the file.</param>
/// <param name="properties">The properties of the file.</param>
public S3File(AmazonS3FileSystem fileSystem, string path, VirtualNodeProperties? properties) : base(path, properties) =>
(_fs, _key) = (fileSystem, path[1..]);
/// <inheritdoc />
protected override async ValueTask<VirtualNodeProperties?> GetPropertiesCoreAsync(CancellationToken cancellationToken)
{
try
{
var metadata = await _fs.AmazonClient
.GetObjectMetadataAsync(
new GetObjectMetadataRequest { BucketName = _fs.BucketName, Key = _key },
cancellationToken)
.ConfigureAwait(false);
return VirtualNodeProperties.CreateFileProperties(
creationTime: default,
lastAccessTime: default,
lastWriteTime: metadata.LastModified.GetValueOrDefault(),
length: metadata.ContentLength);
}
catch (AmazonS3Exception e) when (e.StatusCode == HttpStatusCode.NotFound)
{
return null;
}
}
/// <inheritdoc />
protected override async ValueTask<Stream> OpenReadCoreAsync(CancellationToken cancellationToken)
{
var response = await _fs.AmazonClient
.GetObjectAsync(
new GetObjectRequest { BucketName = _fs.BucketName, Key = _key },
cancellationToken)
.ConfigureAwait(false);
return response.ResponseStream;
}
/// <inheritdoc />
protected override async ValueTask<Stream> OpenWriteCoreAsync(CancellationToken cancellationToken)
{
var response = await _fs.AmazonClient
.InitiateMultipartUploadAsync(_fs.BucketName, _key, cancellationToken)
.ConfigureAwait(false);
return new S3UploadStream(_fs.AmazonClient, _fs.BucketName, _key, response.UploadId);
}
/// <inheritdoc />
protected override async ValueTask WriteCoreAsync(Stream stream, bool overwrite, CancellationToken cancellationToken)
{
var request = new PutObjectRequest
{
BucketName = _fs.BucketName,
Key = _key,
InputStream = stream,
AutoCloseStream = false
};
if (!overwrite)
request.IfNoneMatch = "*";
await _fs.AmazonClient
.PutObjectAsync(request, cancellationToken)
.ConfigureAwait(false);
}
/// <inheritdoc />
protected override async ValueTask DeleteCoreAsync(CancellationToken cancellationToken)
{
try
{
await _fs.AmazonClient
.DeleteObjectAsync(
new DeleteObjectRequest { BucketName = _fs.BucketName, Key = _key },
cancellationToken)
.ConfigureAwait(false);
}
catch (AmazonS3Exception ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
}
}
/// <inheritdoc />
protected override ValueTask CopyToCoreAsync(string destinationPath, bool overwrite, CancellationToken cancellationToken) =>
CopyObjectAsync(_fs.BucketName, _key, _fs.BucketName, destinationPath[1..], overwrite, cancellationToken);
/// <inheritdoc />
protected override ValueTask CopyToCoreAsync(VirtualFile destination, bool overwrite, CancellationToken cancellationToken)
{
if (destination is S3File file)
return CopyObjectAsync(_fs.BucketName, _key, file._fs.BucketName, file._key, overwrite, cancellationToken);
return base.CopyToCoreAsync(destination, overwrite, cancellationToken);
}
/// <summary>
/// Asynchronously copies an object from the source bucket and key to the destination bucket and key.
/// </summary>
/// <param name="sourceBucket">The name of the source S3 bucket.</param>
/// <param name="sourceKey">The key of the source object in the S3 bucket.</param>
/// <param name="destinationBucket">The name of the destination S3 bucket.</param>
/// <param name="destinationKey">The key of the destination object in the S3 bucket.</param>
/// <param name="overwrite">A boolean value indicating whether to overwrite the destination object if it already exists.</param>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that represents the asynchronous copy operation.
/// </returns>
private async ValueTask CopyObjectAsync(string sourceBucket, string sourceKey, string destinationBucket, string destinationKey, bool overwrite, CancellationToken cancellationToken)
{
if (sourceBucket == destinationBucket && sourceKey == destinationKey)
throw new IOException($"Cannot copy a file '{FullName}' to itself.");
// Unfortunately, Amazon S3 does not support destination conditions,
// so we make a separate request to check for the destination object existence.
if (!overwrite)
{
try
{
await _fs.AmazonClient
.GetObjectMetadataAsync(destinationBucket, destinationKey, cancellationToken)
.ConfigureAwait(false);
throw new IOException($"The file '{FullName}' already exists.");
}
catch (AmazonS3Exception e) when (e.StatusCode == HttpStatusCode.NotFound)
{
}
}
var request = new CopyObjectRequest
{
SourceBucket = sourceBucket,
SourceKey = sourceKey,
DestinationBucket = destinationBucket,
DestinationKey = destinationKey
};
await _fs.AmazonClient
.CopyObjectAsync(request, cancellationToken)
.ConfigureAwait(false);
}
}