-
Notifications
You must be signed in to change notification settings - Fork 95
/
Copy pathFileWritingService.cs
137 lines (116 loc) · 4.16 KB
/
FileWritingService.cs
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
namespace Microsoft.ComponentDetection.Common;
using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ComponentDetection.Common.Exceptions;
using Newtonsoft.Json;
/// <inheritdoc />
public sealed class FileWritingService : IFileWritingService
{
/// <summary>
/// The format string used to generate the timestamp for the manifest file.
/// </summary>
public const string TimestampFormatString = "yyyyMMddHHmmssfff";
private readonly ConcurrentDictionary<string, StreamWriter> bufferedStreams = new();
private readonly object lockObject = new();
private readonly string timestamp = DateTime.Now.ToString(TimestampFormatString, CultureInfo.InvariantCulture);
/// <summary>
/// The base path to write files to.
/// If null or empty, the temp path will be used.
/// </summary>
public string BasePath { get; private set; }
/// <inheritdoc />
public void Init(string basePath)
{
if (!string.IsNullOrEmpty(basePath) && !Directory.Exists(basePath))
{
throw new InvalidUserInputException($"The path {basePath} does not exist.", new DirectoryNotFoundException());
}
this.BasePath = string.IsNullOrEmpty(basePath) ? Path.GetTempPath() : basePath;
}
/// <inheritdoc />
public void AppendToFile<T>(string relativeFilePath, T obj)
{
relativeFilePath = this.ResolveFilePath(relativeFilePath);
if (!this.bufferedStreams.TryGetValue(relativeFilePath, out var streamWriter))
{
streamWriter = new StreamWriter(relativeFilePath, true);
_ = this.bufferedStreams.TryAdd(relativeFilePath, streamWriter);
}
var serializer = new JsonSerializer
{
Formatting = Formatting.Indented,
};
serializer.Serialize(streamWriter, obj);
}
/// <inheritdoc />
public void WriteFile(string relativeFilePath, string text)
{
relativeFilePath = this.ResolveFilePath(relativeFilePath);
lock (this.lockObject)
{
File.WriteAllText(relativeFilePath, text);
}
}
/// <inheritdoc />
public async Task WriteFileAsync(string relativeFilePath, string text, CancellationToken cancellationToken = default)
{
relativeFilePath = this.ResolveFilePath(relativeFilePath);
await File.WriteAllTextAsync(relativeFilePath, text, cancellationToken);
}
/// <inheritdoc />
public void WriteFile<T>(FileInfo relativeFilePath, T obj)
{
using var streamWriter = new StreamWriter(relativeFilePath.FullName);
using var jsonWriter = new JsonTextWriter(streamWriter);
var serializer = new JsonSerializer
{
Formatting = Formatting.Indented,
};
serializer.Serialize(jsonWriter, obj);
}
/// <inheritdoc />
public string ResolveFilePath(string relativeFilePath)
{
this.EnsureInit();
if (relativeFilePath.Contains("{timestamp}", StringComparison.Ordinal))
{
relativeFilePath = relativeFilePath.Replace("{timestamp}", this.timestamp, StringComparison.Ordinal);
}
relativeFilePath = Path.Combine(this.BasePath, relativeFilePath);
return relativeFilePath;
}
/// <inheritdoc />
public void Dispose() => this.Dispose(true);
/// <inheritdoc />
public async ValueTask DisposeAsync()
{
foreach (var (filename, streamWriter) in this.bufferedStreams)
{
await streamWriter.DisposeAsync();
_ = this.bufferedStreams.TryRemove(filename, out _);
}
}
private void EnsureInit()
{
if (string.IsNullOrEmpty(this.BasePath))
{
throw new InvalidOperationException("Base path has not yet been initialized in File Writing Service!");
}
}
private void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
foreach (var (filename, streamWriter) in this.bufferedStreams)
{
streamWriter.Dispose();
_ = this.bufferedStreams.TryRemove(filename, out _);
}
}
}