Skip to content

Commit 2cf1b9c

Browse files
authored
fix browser opfs partial writes in ci (#117)
1 parent 31ef438 commit 2cf1b9c

File tree

3 files changed

+49
-8
lines changed

3 files changed

+49
-8
lines changed

Storages/ManagedCode.Storage.Browser/wwwroot/browserStorage.worker.js

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const sessions = new Map();
2+
const maxWriteBlockBytes = 1024 * 1024;
23

34
self.onmessage = async (event) => {
45
const { id, command, payload } = event.data ?? {};
@@ -59,12 +60,7 @@ async function appendChunksAsync(databaseName, blobKey, chunks) {
5960

6061
for (const chunk of Array.isArray(chunks) ? chunks : []) {
6162
const data = chunk.data instanceof Uint8Array ? chunk.data : new Uint8Array(chunk.data ?? []);
62-
const written = session.accessHandle.write(data, { at: session.position });
63-
if (written !== data.byteLength) {
64-
throw new Error(`Short OPFS write for ${blobKey}: wrote ${written} of ${data.byteLength} bytes.`);
65-
}
66-
67-
session.position += written;
63+
writeAllBytes(session, blobKey, data);
6864
}
6965
}
7066

@@ -156,6 +152,37 @@ function getSessionKey(databaseName, blobKey) {
156152
return `${databaseName}::${blobKey}`;
157153
}
158154

155+
function writeAllBytes(session, blobKey, data) {
156+
let offset = 0;
157+
158+
while (offset < data.byteLength) {
159+
const bytesRemaining = data.byteLength - offset;
160+
const bytesToWrite = Math.min(bytesRemaining, maxWriteBlockBytes);
161+
const slice = data.subarray(offset, offset + bytesToWrite);
162+
const written = normalizeBytesWritten(
163+
session.accessHandle.write(slice, { at: session.position }),
164+
bytesToWrite,
165+
blobKey,
166+
session.position);
167+
168+
offset += written;
169+
session.position += written;
170+
}
171+
}
172+
173+
function normalizeBytesWritten(value, expectedBytes, blobKey, position) {
174+
if (!Number.isFinite(value)) {
175+
throw new Error(`Invalid OPFS write result for ${blobKey} at ${position}: ${String(value)}.`);
176+
}
177+
178+
const written = Math.trunc(value);
179+
if (written <= 0 || written > expectedBytes) {
180+
throw new Error(`Invalid OPFS write result for ${blobKey} at ${position}: wrote ${written} of ${expectedBytes} bytes.`);
181+
}
182+
183+
return written;
184+
}
185+
159186
async function getDatabaseDirectoryAsync(databaseName, create) {
160187
const root = await navigator.storage.getDirectory();
161188
return await root.getDirectoryHandle(getDatabaseDirectoryName(databaseName), { create });

Tests/ManagedCode.Storage.BrowserServerHost/Components/Pages/StoragePlayground.razor

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@
134134
MimeType = "application/octet-stream"
135135
});
136136

137+
if (result.IsFailed)
138+
{
139+
largeOutput = $"generated:{stream.Position}";
140+
status = $"large-save-failed:{result.Problem?.Detail}";
141+
return;
142+
}
143+
137144
if (!stream.IsCompleted)
138145
{
139146
largeOutput = $"generated:{stream.Position}";
@@ -145,7 +152,7 @@
145152
var crc = stream.CompletedCrc;
146153

147154
largeOutput = $"expected:{length}:{crc}";
148-
status = result.IsSuccess ? $"large-saved:{length}:{crc}" : $"large-save-failed:{result.Problem?.Detail}";
155+
status = $"large-saved:{length}:{crc}";
149156
Logger.LogInformation("Completed browser storage save for {FileName} ({Bytes} bytes, crc {Crc}) in {ElapsedMilliseconds} ms.",
150157
resolvedFileName,
151158
length,

Tests/ManagedCode.Storage.BrowserWasmHost/Pages/StoragePlayground.razor

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@
133133
MimeType = "application/octet-stream"
134134
});
135135

136+
if (result.IsFailed)
137+
{
138+
largeOutput = $"generated:{stream.Position}";
139+
status = $"large-save-failed:{result.Problem?.Detail}";
140+
return;
141+
}
142+
136143
if (!stream.IsCompleted)
137144
{
138145
largeOutput = $"generated:{stream.Position}";
@@ -144,7 +151,7 @@
144151
var crc = stream.CompletedCrc;
145152

146153
largeOutput = $"expected:{length}:{crc}";
147-
status = result.IsSuccess ? $"large-saved:{length}:{crc}" : $"large-save-failed:{result.Problem?.Detail}";
154+
status = $"large-saved:{length}:{crc}";
148155
Logger.LogInformation("Completed browser storage save for {FileName} ({Bytes} bytes, crc {Crc}) in {ElapsedMilliseconds} ms.",
149156
resolvedFileName,
150157
length,

0 commit comments

Comments
 (0)