Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
FileCacheManager.cs
1using System;
3using System.Linq;
4using System.Text;
5using System.Threading.Tasks;
7using Waher.Content;
10
12{
13 internal class FileCacheManager
14 {
15 private readonly string folderPath;
16 private readonly TimeSpan defaultExpiry;
17
18 private static int evictionPerformed = 0; // 0 = not yet, 1 = done
19
20
21 public FileCacheManager(string folderName, TimeSpan defaultExpiry)
22 {
23 this.folderPath = Path.Combine(FileSystem.CacheDirectory, folderName);
24 this.defaultExpiry = defaultExpiry;
25
26 if (!Directory.Exists(this.folderPath))
27 Directory.CreateDirectory(this.folderPath);
28 }
29
30 public async Task EvictOldEntries()
31 {
32 try
33 {
34 // Only allow the first caller to actually perform eviction
35 // This is not really needed,
36 // but added justin case there ever is a situations where the cache contains a ridicoulus amount of files
37 if (Interlocked.Exchange(ref evictionPerformed, 1) == 1)
38 return;
39
40 IEnumerable<CacheEntry> Expired = await Database.FindDelete<CacheEntry>(
41 new FilterFieldLesserOrEqualTo("Expires", DateTime.UtcNow));
42
43 foreach (CacheEntry Entry in Expired)
44 {
45 try { File.Delete(Entry.LocalFileName); }
46 catch { /* log if desired */ }
47 }
48 }
49 catch
50 {
51 // Ignore
52 }
53 }
54
55 public async Task<(byte[]?, string)> TryGet(string Key)
56 {
57 if (string.IsNullOrEmpty(Key))
58 return (null, string.Empty);
59
60 CacheEntry? Entry = await Database.FindFirstDeleteRest<CacheEntry>(
61 new FilterFieldEqualTo("Url", Key));
62
63 if (Entry is null ||
64 !File.Exists(Entry.LocalFileName))
65 {
66 if (Entry is not null)
67 {
68 try { File.Delete(Entry.LocalFileName); }
69 catch { }
70 await Database.Delete(Entry);
71 await Database.Provider.Flush();
72 }
73 return (null, string.Empty);
74 }
75
76 // Only return non-Expired entries from this method.
77 if (DateTime.UtcNow >= Entry.Expires)
78 return (null, string.Empty);
79
80 return (File.ReadAllBytes(Entry.LocalFileName), Entry.ContentType);
81 }
82
87 public async Task<(byte[]? Data, string ContentType, bool IsExpired)> TryGetWithExpiry(string Key)
88 {
89 if (string.IsNullOrEmpty(Key))
90 return (null, string.Empty, false);
91
92 CacheEntry? Entry = await Database.FindFirstDeleteRest<CacheEntry>(
93 new FilterFieldEqualTo("Url", Key));
94
95 if (Entry is null)
96 return (null, string.Empty, false);
97
98 if (!File.Exists(Entry.LocalFileName))
99 {
100 try { await Database.Delete(Entry); }
101 catch { }
102 await Database.Provider.Flush();
103 return (null, string.Empty, false);
104 }
105
106 bool Expired = DateTime.UtcNow >= Entry.Expires && Entry.Expires != DateTime.MaxValue;
107 return (File.ReadAllBytes(Entry.LocalFileName), Entry.ContentType, Expired);
108 }
109
110 public async Task AddOrUpdate(string key, string parentId, bool permanent,
111 byte[] data, string contentType)
112 {
113 if (string.IsNullOrEmpty(key) || data is null || string.IsNullOrEmpty(contentType))
114 return;
115
116 DateTime Expires = permanent
117 ? DateTime.MaxValue
118 : DateTime.UtcNow + this.defaultExpiry;
119
120 CacheEntry? Existing = await Database.FindFirstDeleteRest<CacheEntry>(
121 new FilterFieldEqualTo("Url", key));
122
123 string Ext = InternetContent.GetFileExtension(contentType);
124 string Filename = Path.Combine(this.folderPath, $"{Guid.NewGuid():N}.{Ext}");
125
126 await File.WriteAllBytesAsync(Filename, data);
127
128 if (Existing is null)
129 {
130 await Database.Insert(new CacheEntry
131 {
132 Url = key,
133 ParentId = parentId ?? string.Empty,
134 LocalFileName = Filename,
135 ContentType = contentType,
136 Expires = Expires
137 });
138 }
139 else
140 {
141 try { File.Delete(Existing.LocalFileName); }
142 catch { }
143
144 Existing.LocalFileName = Filename;
145 Existing.ContentType = contentType;
146 Existing.ParentId = parentId ?? string.Empty;
147 Existing.Expires = Expires;
148
149 await Database.Update(Existing);
150 }
151
152 await Database.Provider.Flush();
153 }
154
155 public async Task<bool> Remove(string Key)
156 {
157 if (string.IsNullOrEmpty(Key))
158 return false;
159
160 CacheEntry? Entry = await Database.FindFirstDeleteRest<CacheEntry>(
161 new FilterFieldEqualTo("Url", Key));
162
163 if (Entry is null)
164 return false;
165
166 try { File.Delete(Entry.LocalFileName); }
167 catch
168 {
169 //Ignore as it may not exist or be accessible. (User clearing data etc)
170 }
171
172
173 await Database.Delete(Entry);
174 await Database.Provider.Flush();
175 return true;
176 }
177
178 public async Task MakeTemporary(string parentId)
179 {
180 foreach (CacheEntry Entry in await Database.Find<CacheEntry>(
181 new FilterFieldEqualTo("ParentId", parentId)))
182 {
183 if (Entry.Expires == DateTime.MaxValue)
184 {
185 Entry.Expires = DateTime.UtcNow + this.defaultExpiry;
186 await Database.Update(Entry);
187 }
188 }
189 await Database.Provider.Flush();
190 }
191
192 public async Task MakePermanent(string parentId)
193 {
194 foreach (CacheEntry Entry in await Database.Find<CacheEntry>(
195 new FilterFieldEqualTo("ParentId", parentId)))
196 {
197 if (Entry.Expires != DateTime.MaxValue)
198 {
199 Entry.Expires = DateTime.MaxValue;
200 await Database.Update(Entry);
201 }
202 }
203 await Database.Provider.Flush();
204 }
205
206 public async Task<int> RemoveByParentId(string parentId)
207 {
208 int Removed = 0;
209 foreach (CacheEntry Entry in await Database.Find<CacheEntry>(
210 new FilterFieldEqualTo("ParentId", parentId)))
211 {
212 try { File.Delete(Entry.LocalFileName); }
213 catch { /* ignore */ }
214
215 await Database.Delete(Entry);
216 Removed++;
217 }
218
219 await Database.Provider.Flush();
220 return Removed;
221 }
222 }
223
224}
Contains information about a file in the local cache.
Definition: CacheEntry.cs:15
CaseInsensitiveString LocalFileName
Local file name.
Definition: CacheEntry.cs:64
CaseInsensitiveString ContentType
Content-Type
Definition: CacheEntry.cs:82
DateTime Expires
Timestamp of entry
Definition: CacheEntry.cs:44
Static class managing encoding and decoding of internet content.
static string GetFileExtension(string ContentType)
Gets the file extension of an item, given its content type. It uses the TryGetFileExtension to see if...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
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:1442
static IDatabaseProvider Provider
Registered database provider.
Definition: Database.cs:59
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:1211
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:1291
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:238
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:97
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.
Task Flush()
Persists any pending changes.
ContentType
DTLS Record content type.
Definition: Enumerations.cs:11