-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathVirtualFile.cs
More file actions
153 lines (122 loc) · 7.15 KB
/
VirtualFile.cs
File metadata and controls
153 lines (122 loc) · 7.15 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
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ITHit.FileSystem;
using ITHit.FileSystem.Windows;
using ITHit.FileSystem.Samples.Common.Windows;
namespace VirtualDrive
{
/// <inheritdoc cref="IFile"/>
public class VirtualFile : VirtualFileSystemItem, IFileWindows
{
/// <summary>
/// Creates instance of this class.
/// </summary>
/// <param name="path">File path in the user file system.</param>
/// <param name="remoteStorageItemId">Remote storage item ID.</param>
/// <param name="engine">Engine instance.</param>
/// <param name="logger">Logger.</param>
public VirtualFile(string path, byte[] remoteStorageItemId, VirtualEngine engine, ILogger logger) : base(path, remoteStorageItemId, engine, logger)
{
}
/// <inheritdoc/>
public async Task OpenCompletionAsync(IOperationContext operationContext, IResultContext context, CancellationToken cancellationToken)
{
Logger.LogDebug($"{nameof(IFileWindows)}.{nameof(OpenCompletionAsync)}()", UserFileSystemPath, default, operationContext);
}
/// <inheritdoc/>
public async Task CloseCompletionAsync(IOperationContext operationContext, IResultContext context, CancellationToken cancellationToken)
{
Logger.LogDebug($"{nameof(IFileWindows)}.{nameof(CloseCompletionAsync)}()", UserFileSystemPath, default, operationContext);
}
/// <inheritdoc/>
public async Task<IFileMetadata> ReadAsync(Stream output, long offset, long length, IFileMetadata metadata, ITransferDataOperationContext operationContext, ITransferDataResultContext resultContext, CancellationToken cancellationToken)
{
// On Windows this method has a 60 sec timeout.
// To process longer requests and reset the timeout timer write to the output stream or call the resultContext.ReportProgress() or resultContext.ReturnDataAsync() methods.
Logger.LogMessage($"{nameof(IFile)}.{nameof(ReadAsync)}({offset}, {length})", UserFileSystemPath, default, operationContext);
if (!Mapping.TryGetRemoteStoragePathById(RemoteStorageItemId, out string remoteStoragePath)) return null;
cancellationToken.Register(() => { Logger.LogMessage($"{nameof(IFile)}.{nameof(ReadAsync)}({offset}, {length}) cancelled", UserFileSystemPath, default, operationContext); });
using (FileStream stream = new FileInfo(remoteStoragePath).Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
{
if (output.Length != stream.Length)
throw new StatusException(CloudFileStatus.STATUS_CLOUD_FILE_UNSUCCESSFUL);
stream.Seek(offset, SeekOrigin.Begin);
const int bufferSize = 0x500000; // 5Mb. Buffer size must be multiple of 4096 bytes for optimal performance.
try
{
await stream.CopyToAsync(output, bufferSize, length, cancellationToken);
}
catch (OperationCanceledException)
{
// Operation was canceled by the calling Engine.StopAsync() or the operation timeout occured.
Logger.LogDebug($"{nameof(ReadAsync)}({offset}, {length}) canceled", UserFileSystemPath, default, operationContext, metadata);
}
}
// Return an updated item to the Engine.
// In the returned data set the following fields:
// - Content eTag. The Engine will store it to determine if the file content should be updated.
// - Medatdata eTag. The Engine will store it to determine if the item metadata should be updated.
return null;
}
/// <inheritdoc/>
public async Task ValidateDataAsync(long offset, long length, IValidateDataOperationContext operationContext, IValidateDataResultContext resultContext)
{
// This method has a 60 sec timeout.
// To process longer requests and reset the timeout timer call the ReturnValidationResult()
// method or IResultContext.ReportProgress() method.
Logger.LogMessage($"{nameof(IFile)}.{nameof(ValidateDataAsync)}({offset}, {length})", UserFileSystemPath, default, operationContext);
bool isValid = true;
resultContext.ReturnValidationResult(offset, length, isValid);
}
/// <inheritdoc/>
public async Task<IFileMetadata> WriteAsync(IFileMetadata metadata, Stream content = null, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
{
Logger.LogMessage($"{nameof(IFile)}.{nameof(WriteAsync)}()", UserFileSystemPath, default, operationContext);
if (!Mapping.TryGetRemoteStoragePathById(RemoteStorageItemId, out string remoteStoragePath)) return null;
// Send the ETag to the server as part of the update to ensure
// the file in the remote storge is not modified since last read.
// operationContext.Properties.TryGetETag(out string oldEtag);
// Send the lock-token to the server as part of the update.
// Here we read the lock-token for demo purposes only.
operationContext.Properties.TryGetCurrentUserLockToken(out string lockToken);
FileInfo remoteStorageItem = new FileInfo(remoteStoragePath);
if (content != null)
{
// Upload file content to the remote storage.
using (FileStream remoteStorageStream = remoteStorageItem.Open(FileMode.Open, FileAccess.Write, FileShare.Delete))
{
await content.CopyToAsync(remoteStorageStream, cancellationToken);
remoteStorageStream.SetLength(content.Length);
}
}
// Update remote storage file metadata.
if (metadata.Attributes.HasValue)
{
remoteStorageItem.Attributes = metadata.Attributes.Value & ~FileAttributes.ReadOnly;
}
if (metadata.CreationTime.HasValue)
{
remoteStorageItem.CreationTimeUtc = metadata.CreationTime.Value.UtcDateTime;
}
if (metadata.LastWriteTime.HasValue)
{
remoteStorageItem.LastWriteTimeUtc = metadata.LastWriteTime.Value.UtcDateTime;
}
if (metadata.LastAccessTime.HasValue)
{
remoteStorageItem.LastAccessTimeUtc = metadata.LastAccessTime.Value.UtcDateTime;
}
if (metadata.Attributes.HasValue)
{
remoteStorageItem.Attributes = metadata.Attributes.Value;
}
// Return an updated item to the Engine.
// In the returned data set the following fields:
// - Content eTag. The Engine will store it to determine if the file content should be updated.
// - Medatdata eTag. The Engine will store it to determine if the item metadata should be updated.
return null;
}
}
}