Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
AttachmentCacheService.cs
5
7{
9 [Singleton]
10 internal sealed partial class AttachmentCacheService : LoadableService, IAttachmentCacheService
11 {
12 private static readonly TimeSpan expiryTemporary = TimeSpan.FromHours(24);
13 private const string cacheFolderName = "Attachments";
14
18 public AttachmentCacheService()
19 {
20 }
21
23 public override async Task Load(bool IsResuming, CancellationToken CancellationToken)
24 {
25 if (this.BeginLoad(IsResuming, CancellationToken))
26 {
27 try
28 {
29 CreateCacheFolderIfNeeded();
30
31 if (!IsResuming)
32 await EvictOldEntries();
33
34 this.EndLoad(true);
35 }
36 catch (Exception ex)
37 {
38 ServiceRef.LogService.LogException(ex);
39 this.EndLoad(false);
40 }
41 }
42 }
43
49 public async Task<(byte[]?, string)> TryGet(string Url)
50 {
51 try
52 {
53 if (string.IsNullOrWhiteSpace(Url) || !Uri.IsWellFormedUriString(Url, UriKind.RelativeOrAbsolute))
54 return (null, string.Empty);
55
56 CacheEntry Entry = await Database.FindFirstDeleteRest<CacheEntry>(new FilterFieldEqualTo("Url", Url));
57
58 if (Entry is null)
59 return (null, string.Empty);
60
61 bool Exists = File.Exists(Entry.LocalFileName);
62
63 if (DateTime.UtcNow >= Entry.Expires || !Exists)
64 {
65 if (Exists)
66 File.Delete(Entry.LocalFileName);
67
68 await Database.Delete(Entry);
69 await Database.Provider.Flush();
70
71 return (null, string.Empty);
72 }
73
74 return (File.ReadAllBytes(Entry.LocalFileName), Entry.ContentType);
75 }
76 catch (Exception ex)
77 {
78 ServiceRef.LogService.LogException(ex);
79 return (null, string.Empty);
80 }
81 }
82
91 public async Task Add(string Url, string ParentId, bool Permanent, byte[] Data, string ContentType)
92 {
93 if (string.IsNullOrWhiteSpace(Url) ||
94 !Uri.IsWellFormedUriString(Url, UriKind.RelativeOrAbsolute) ||
95 Data is null ||
96 string.IsNullOrWhiteSpace(ContentType))
97 {
98 return;
99 }
100
101 string CacheFolder = CreateCacheFolderIfNeeded();
102
103 CacheEntry Entry = await Database.FindFirstDeleteRest<CacheEntry>(new FilterFieldEqualTo("Url", Url));
104 DateTime Expires = Permanent ? DateTime.MaxValue : (DateTime.UtcNow + expiryTemporary);
105
106 if (Entry is null)
107 {
108 Entry = new CacheEntry()
109 {
110 Expires = Expires,
111 ParentId = ParentId,
112 LocalFileName = Path.Combine(CacheFolder, Guid.NewGuid().ToString() + ".bin"),
113 Url = Url,
114 ContentType = ContentType
115 };
116
117 await Database.Insert(Entry);
118 }
119 else
120 {
121 Entry.Expires = Expires;
122 Entry.ParentId = ParentId;
123 Entry.ContentType = ContentType;
124
125 await Database.Update(Entry);
126 }
127
128 File.WriteAllBytes(Entry.LocalFileName, Data);
129
130 await Database.Provider.Flush();
131 }
132
138 public async Task<bool> Remove(string Url)
139 {
140 if (string.IsNullOrWhiteSpace(Url) || !Uri.IsWellFormedUriString(Url, UriKind.RelativeOrAbsolute))
141 return false;
142
143 CacheEntry Entry = await Database.FindFirstDeleteRest<CacheEntry>(new FilterFieldEqualTo("Url", Url));
144 if (Entry is null)
145 return false;
146
147 try
148 {
149 if (File.Exists(Entry.LocalFileName))
150 File.Delete(Entry.LocalFileName);
151
152 await Database.Delete(Entry);
153 }
154 catch (Exception ex)
155 {
156 ServiceRef.LogService.LogException(ex);
157 }
158
159 return true;
160 }
161
167 public async Task<bool> RemoveAttachments(Attachment[]? Attachments)
168 {
169 if (Attachments is null)
170 return false;
171
172 bool Removed = false;
173
174 foreach (Attachment Attachment in Attachments)
175 {
176 if (!Attachment.ContentType.StartsWith("image/", StringComparison.Ordinal))
177 continue;
178
179 if (await this.Remove(Attachment.Url))
180 Removed = true;
181 }
182
183 return Removed;
184 }
185
186 private static string CreateCacheFolderIfNeeded()
187 {
188 string CacheFolder = GetCacheFolder();
189
190 if (!Directory.Exists(CacheFolder))
191 Directory.CreateDirectory(CacheFolder);
192
193 return CacheFolder;
194 }
195
196 private static string GetCacheFolder()
197 {
198 return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), cacheFolderName);
199 }
200
201 private static async Task EvictOldEntries()
202 {
203 try
204 {
205 foreach (CacheEntry Entry in await Database.FindDelete<CacheEntry>(
206 new FilterFieldLesserOrEqualTo("Expires", DateTime.UtcNow)))
207 {
208 try
209 {
210 if (File.Exists(Entry.LocalFileName))
211 File.Delete(Entry.LocalFileName);
212 }
213 catch (Exception ex)
214 {
215 ServiceRef.LogService.LogException(ex);
216 }
217 }
218 }
219 catch (Exception ex)
220 {
221 ServiceRef.LogService.LogException(ex);
222 }
223 }
224
229 public async Task MakeTemporary(string ParentId)
230 {
231 foreach (CacheEntry Entry in await Database.Find<CacheEntry>(new FilterFieldEqualTo("ParentId", ParentId)))
232 {
233 if (Entry.Expires == DateTime.MaxValue)
234 {
235 Entry.Expires = DateTime.UtcNow + expiryTemporary;
236 await Database.Update(Entry);
237 }
238 }
239
240 await Database.Provider.Flush();
241 }
242
247 public async Task MakePermanent(string ParentId)
248 {
249 foreach (CacheEntry Entry in await Database.Find<CacheEntry>(new FilterFieldEqualTo("ParentId", ParentId)))
250 {
251 if (Entry.Expires != DateTime.MaxValue)
252 {
253 Entry.Expires = DateTime.MaxValue;
254 await Database.Update(Entry);
255 }
256 }
257
258 await Database.Provider.Flush();
259 }
260 }
261}
Contains information about a file in the local cache.
Definition: CacheEntry.cs:15
CaseInsensitiveString ContentType
Content-Type
Definition: CacheEntry.cs:82
DateTime Expires
Timestamp of entry
Definition: CacheEntry.cs:44
CaseInsensitiveString LocalFileName
Local file name.
Definition: CacheEntry.cs:64
bool BeginLoad(bool IsResuming, CancellationToken CancellationToken)
Sets the IsLoading flag if the service isn't already loading.
void EndLoad(bool isLoaded)
Sets the IsLoading and IsLoaded flags and fires an event representing the current load state of the s...
bool IsResuming
If App is resuming service.
Base class that references services in the app.
Definition: ServiceRef.cs:31
static ILogService LogService
Log service.
Definition: ServiceRef.cs:91
Contains a reference to an attachment assigned to a legal object.
Definition: Attachment.cs:9
string ContentType
Internet Content Type of binary attachment.
Definition: Attachment.cs:47
string Url
URL to retrieve attachment, if provided.
Definition: Attachment.cs:65
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
static Task< IEnumerable< object > > FindDelete(string Collection, params string[] SortOrder)
Finds objects in a given collection and deletes them in the same atomic operation.
Definition: Database.cs:879
static IDatabaseProvider Provider
Registered database provider.
Definition: Database.cs:57
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:717
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:247
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
This filter selects objects that have a named field equal to a given value.
This filter selects objects that have a named field lesser or equal to a given value.
Represents a service that caches images downloaded via http calls. They are stored for a certain dura...
Task Flush()
Persists any pending changes.