Skip to content

Commit 06a1f2b

Browse files
Merge pull request #5 from KristofferStrube/feature/update-version-of-blazor-streams
Update version of Blazor.Streams
2 parents f62fd7a + c155942 commit 06a1f2b

28 files changed

+1032
-268
lines changed

.EditorConfig

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
[*]
2+
# All files
3+
dotnet_style_qualification_for_field = false
4+
dotnet_style_qualification_for_property = false
5+
dotnet_style_qualification_for_method = false
6+
dotnet_style_qualification_for_event = false
7+
dotnet_diagnostic.IDE0003.severity = warning
8+
dotnet_style_predefined_type_for_locals_parameters_members = true
9+
dotnet_style_predefined_type_for_member_access = true
10+
dotnet_diagnostic.IDE0049.severity = suggestion
11+
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
12+
dotnet_diagnostic.IDE0036.severity = error
13+
dotnet_style_require_accessibility_modifiers = always
14+
dotnet_diagnostic.IDE0040.severity = warning
15+
dotnet_style_readonly_field = true
16+
dotnet_diagnostic.IDE0044.severity = error
17+
csharp_prefer_static_local_function = true
18+
dotnet_diagnostic.IDE0062.severity = warning
19+
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
20+
dotnet_diagnostic.IDE0047.severity = warning
21+
dotnet_diagnostic.IDE0048.severity = warning
22+
dotnet_diagnostic.IDE0010.severity = error
23+
dotnet_style_object_initializer = true
24+
dotnet_diagnostic.IDE0017.severity = suggestion
25+
csharp_style_inlined_variable_declaration = true
26+
dotnet_diagnostic.IDE0018.severity = suggestion
27+
dotnet_style_collection_initializer = true
28+
dotnet_diagnostic.IDE0028.severity = warning
29+
dotnet_style_prefer_auto_properties = true
30+
dotnet_diagnostic.IDE0032.severity = suggestion
31+
dotnet_style_explicit_tuple_names = true
32+
dotnet_diagnostic.IDE0033.severity = warning
33+
csharp_prefer_simple_default_expression = false
34+
dotnet_diagnostic.IDE0034.severity = warning
35+
dotnet_style_prefer_inferred_tuple_names = true
36+
dotnet_style_prefer_inferred_anonymous_type_member_names = true
37+
dotnet_diagnostic.IDE0037.severity = suggestion
38+
csharp_style_prefer_local_over_anonymous_function = true
39+
dotnet_diagnostic.IDE0039.severity = warning
40+
csharp_style_deconstructed_variable_declaration = true
41+
dotnet_diagnostic.IDE0042.severity = suggestion
42+
dotnet_style_prefer_conditional_expression_over_assignment = true
43+
dotnet_diagnostic.IDE0045.severity = warning
44+
dotnet_style_prefer_conditional_expression_over_return = true
45+
dotnet_diagnostic.IDE0046.severity = warning
46+
dotnet_style_prefer_compound_assignment = true
47+
dotnet_diagnostic.IDE0054.severity = warning
48+
dotnet_diagnostic.IDE0074.severity = warning
49+
csharp_style_prefer_index_operator = true
50+
dotnet_diagnostic.IDE0056.severity = warning
51+
csharp_style_prefer_range_operator = true
52+
dotnet_diagnostic.IDE0057.severity = warning
53+
dotnet_diagnostic.IDE0070.severity = error
54+
dotnet_style_prefer_simplified_interpolation = true
55+
dotnet_diagnostic.IDE0071.severity = warning
56+
dotnet_diagnostic.IDE0072.severity = error
57+
dotnet_style_prefer_simplified_boolean_expressions = true
58+
dotnet_diagnostic.IDE0075.severity = warning
59+
dotnet_diagnostic.IDE0082.severity = error
60+
csharp_style_implicit_object_creation_when_type_is_apparent = true
61+
dotnet_diagnostic.IDE0090.severity = error
62+
dotnet_diagnostic.IDE0180.severity = warning
63+
csharp_style_namespace_declarations = file_scoped
64+
dotnet_diagnostic.IDE0160.severity = error
65+
dotnet_diagnostic.IDE0161.severity = error
66+
csharp_style_throw_expression = true
67+
dotnet_diagnostic.IDE0016.severity = warning
68+
dotnet_style_coalesce_expression = true
69+
dotnet_diagnostic.IDE0029.severity = warning
70+
dotnet_diagnostic.IDE0030.severity = warning
71+
dotnet_style_null_propagation = true
72+
dotnet_diagnostic.IDE0031.severity = warning
73+
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
74+
dotnet_diagnostic.IDE0041.severity = warning
75+
csharp_style_prefer_null_check_over_type_check = true
76+
dotnet_diagnostic.IDE0150.severity = warning
77+
csharp_style_conditional_delegate_call = false
78+
dotnet_diagnostic.IDE1005.severity = warning
79+
csharp_style_var_for_built_in_types = false
80+
csharp_style_var_when_type_is_apparent = true
81+
csharp_style_var_elsewhere = false
82+
dotnet_diagnostic.IDE0007.severity = warning
83+
dotnet_diagnostic.IDE0008.severity = warning
84+
dotnet_diagnostic.IDE0001.severity = error
85+
dotnet_diagnostic.IDE0002.severity = error
86+
dotnet_diagnostic.IDE0004.severity = error
87+
dotnet_diagnostic.IDE0005.severity = error
88+
dotnet_diagnostic.IDE0035.severity = warning
89+
dotnet_diagnostic.IDE0051.severity = warning
90+
dotnet_diagnostic.IDE0052.severity = warning
91+
csharp_style_unused_value_expression_statement_preference = discard_variable
92+
dotnet_diagnostic.IDE0058.severity = warning
93+
csharp_style_unused_value_assignment_preference = discard_variable
94+
dotnet_diagnostic.IDE0059.severity = warning

.github/workflows/main.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ jobs:
1414
# Checkout the code
1515
- uses: actions/checkout@v2
1616

17-
# Install .NET 7.0 SDK
18-
- name: Setup .NET 7 preview
19-
uses: actions/setup-dotnet@v1
17+
# Install .NET 8.0 SDK
18+
- name: Setup .NET 8 preview
19+
uses: actions/setup-dotnet@v3
2020
with:
21-
dotnet-version: '7.0.x'
21+
dotnet-version: '8.0.x'
2222
include-prerelease: true
2323

2424
# Generate the website

README.md

+14-12
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ You also need to reference the package in order to use it in your pages. This ca
4141
Most of this library is wrapper classes which can be instantiated from your code using the static `Create` and `CreateAsync` methods on the wrapper classes.
4242
An example could be to create an instance of a `Blob` that contains the text `"Hello World!"` and gets its `Size` and `Type`, read it as a `ReadableStream`, read as text directly, and slice it into a new `Blob` like this.
4343
```csharp
44-
Blob blob = await Blob.CreateAsync(
44+
await using Blob blob = await Blob.CreateAsync(
4545
JSRuntime,
4646
blobParts: new BlobPart[] {
4747
"Hello ",
@@ -51,13 +51,13 @@ Blob blob = await Blob.CreateAsync(
5151
);
5252
ulong size = await blob.GetSizeAsync(); // 12
5353
string type = await blob.GetTypeAsync(); // "text/plain"
54-
ReadableStream stream = await blob.StreamAsync();
54+
await using ReadableStream stream = await blob.StreamAsync();
5555
string text = await blob.TextAsync(); // "Hello World!"
56-
Blob worldBlob = await blob.SliceAsync(6, 11); // Blob containing "World"
56+
await using Blob worldBlob = await blob.SliceAsync(6, 11); // Blob containing "World"
5757
```
5858
All creator methods take an `IJSRuntime` instance as the first parameter. The above sample will work in both Blazor Server and Blazor WebAssembly. If we only want to work with Blazor WebAssembly we can use the `InProcess` variant of the wrapper class. This is equivalent to the relationship between `IJSRuntime` and `IJSInProcessRuntime`. We can recreate the above sample using the `BlobInProcess` which will simplify some of the methods we can call on the `Blob` and how we access attributes.
5959
```csharp
60-
BlobInProcess blob = await BlobInProcess.CreateAsync(
60+
await using BlobInProcess blob = await BlobInProcess.CreateAsync(
6161
JSRuntime,
6262
blobParts: new BlobPart[] {
6363
"Hello ",
@@ -67,21 +67,21 @@ BlobInProcess blob = await BlobInProcess.CreateAsync(
6767
);
6868
ulong size = blob.Size; // 12
6969
string type = blob.Type; // "text/plain"
70-
ReadableStreamInProcess stream = await blob.StreamAsync();
70+
await using ReadableStreamInProcess stream = await blob.StreamAsync();
7171
string text = await blob.TextAsync(); // "Hello World!"
72-
BlobInProcess worldBlob = blob.Slice(6, 11); // BlobInProcess containing "World"
72+
await using BlobInProcess worldBlob = blob.Slice(6, 11); // BlobInProcess containing "World"
7373
```
7474
Some of the methods wrap a `Promise` so even in the `InProcess` variant we need to await it like we see for `TextAsync` above.
7575

7676
If you have an `IJSObjectReference` or an `IJSInProcessObjectReference` for a type equivalent to one of the classes wrapped in this package then you can construct a wrapper for it using another set of overloads of the static `Create` and `CreateAsync` methods on the appropriate class. In the below example we create wrapper instances from existing JS references to a `File` object.
7777
```csharp
7878
// Blazor Server compatible.
7979
IJSObjectReference jSFile; // JS Reference from other package or your own JSInterop.
80-
File file = File.Create(JSRuntime, jSFile);
80+
await using File file = File.CreateAsync(JSRuntime, jSFile, new() { DisposesJSReference = true });
8181

8282
// InProcess only supported in Blazor WebAssembly.
8383
IJSInProcessObjectReference jSFileInProcess; // JS Reference from other package or your own JSInterop.
84-
FileInProcess fileInProcess = await File.CreateAsync(JSRuntime, jSFileInProcess);
84+
await using FileInProcess fileInProcess = await File.CreateAsync(JSRuntime, jSFileInProcess);
8585
```
8686

8787
## Add to service collection
@@ -127,11 +127,13 @@ You can likewise add the `InProcess` variant of the service (`IURLServiceInProce
127127
Feel free to open issues on the repository if you find any errors with the package or have wishes for features.
128128

129129
# Related repositories
130-
This project uses the *Blazor.Streams* package to return a rich `ReadableStream` from the `StreamAsync` method on a `Blob`.
131-
- https://github.com/KristofferStrube/Blazor.Streams
130+
The library uses the following other packages to support its features:
131+
- https://github.com/KristofferStrube/Blazor.Streams (To return a rich `ReadableStream` from the `StreamAsync` method on a `Blob`)
132+
- https://github.com/KristofferStrube/Blazor.DOM (To implement `FileReader` which extends `EventTarget` and to implement `ProgressEvent` which extends `Event`)
132133

133-
This project is used in the *Blazor.FileSystem* package to return a rich `File` object when getting the `File` from a `FileSystemFileHandle` and when writing a `Blob` to a `FileSystemWritableFileSystem`.
134-
- https://github.com/KristofferStrube/Blazor.FileSystemAccess
134+
The library is used in the following other packages to support their features:
135+
- https://github.com/KristofferStrube/Blazor.FileSystem (to return a rich `File` object when getting the `File` from a `FileSystemFileHandle` and when writing a `Blob` to a `FileSystemWritableFileSystem`)
136+
- https://github.com/KristofferStrube/Blazor.MediaStreamRecording (to get the data from a `BlobEvent` which is created when a chunk of a stream has been recorded)
135137

136138
# Related articles
137139
This repository was build with inspiration and help from the following series of articles:

samples/KristofferStrube.Blazor.FileAPI.WasmExample/KristofferStrube.Blazor.FileAPI.WasmExample.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
22

33
<PropertyGroup>
4-
<TargetFramework>net7.0</TargetFramework>
4+
<TargetFramework>net8.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.0-rc.1.22427.2" />
11-
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.0-rc.1.22427.2" PrivateAssets="all" />
10+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" />
11+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.10" PrivateAssets="all" />
1212
</ItemGroup>
1313

1414
<ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@page "/FileReaderSample"
22
@using System.Text
3+
@implements IAsyncDisposable
34

45
@inject IJSRuntime JSRuntime
56
@inject HttpClient HttpClient
@@ -37,70 +38,89 @@ We then use the methods of the <code>FileReader</code> interface to read the <co
3738
);
3839
}
3940

40-
private void GetProgress(ProgressEventInProcess eventArgs, string prepend)
41+
private void GetProgress(ProgressEventInProcess eventArgs)
4142
{
43+
var time = eventArgs.TimeStamp;
4244
var progress = eventArgs.LengthComputable ? $"({eventArgs.Loaded}/{eventArgs.Total})" : "";
43-
log += $"{prepend}: {progress}\n";
45+
log += $"{time:F2} - {eventArgs.Type}: {progress}\n";
4446
StateHasChanged();
4547
}
4648

47-
48-
private async Task GetProgressAsync(ProgressEvent eventArgs, string prepend)
49+
private async Task GetProgressAsync(ProgressEvent eventArgs)
4950
{
51+
var time = await eventArgs.GetTimeStampAsync();
52+
var type = await eventArgs.GetTypeAsync();
5053
var progress = await eventArgs.GetLengthComputableAsync() ? $"({await eventArgs.GetLoadedAsync()}/{await eventArgs.GetTotalAsync()})" : "";
51-
log += $"{prepend}: {progress}\n";
54+
log += $"{time:F2} - {type}: {progress}\n";
5255
StateHasChanged();
5356
}
5457

5558
public async Task ReadAsArrayBufferAsync()
5659
{
5760
log = "";
5861
var fileReader = await FileReader.CreateAsync(JSRuntime);
59-
fileReader.OnLoadStart = async (e) => await GetProgressAsync(e, "OnLoadStart");
60-
fileReader.OnProgress = async (e) => await GetProgressAsync(e, "OnProgress");
61-
fileReader.OnLoad = async (e) => await GetProgressAsync(e, "OnLoad");
62-
fileReader.OnAbort = async (e) => await GetProgressAsync(e, "OnAbort");
63-
fileReader.OnError = async (e) => await GetProgressAsync(e, "OnError");
64-
fileReader.OnLoadEnd = async (e) =>
62+
63+
await using var eventListener = await EventListener<ProgressEvent>.CreateAsync(JSRuntime, GetProgressAsync);
64+
await using var loadEndEventLister = await EventListener<ProgressEvent>.CreateAsync(JSRuntime, async e =>
6565
{
6666
imageUrl = "data:image/png;base64," + Convert.ToBase64String(await fileReader.GetResultAsByteArrayAsync() ?? new byte[0]);
67-
await GetProgressAsync(e, "OnLoadEnd");
68-
};
67+
await GetProgressAsync(e);
68+
});
69+
70+
await fileReader.AddOnLoadStartEventListenerAsync(eventListener);
71+
await fileReader.AddOnProgressEventListenerAsync(eventListener);
72+
await fileReader.AddOnLoadEventListenerAsync(eventListener);
73+
await fileReader.AddOnAbortEventListenerAsync(eventListener);
74+
await fileReader.AddOnErrorEventListenerAsync(eventListener);
75+
await fileReader.AddOnLoadEndEventListenerAsync(loadEndEventLister);
76+
6977
await fileReader.ReadAsArrayBufferAsync(blob!);
7078
}
7179

7280
public async Task ReadAsArrayBufferInProcessAsync()
7381
{
7482
log = "";
7583
var fileReader = await FileReaderInProcess.CreateAsync(JSRuntime);
76-
fileReader.OnLoadStart = (e) => GetProgress(e, "OnLoadStart");
77-
fileReader.OnProgress = (e) => GetProgress(e, "OnProgress");
78-
fileReader.OnLoad = (e) => GetProgress(e, "OnLoad");
79-
fileReader.OnAbort = (e) => GetProgress(e, "OnAbort");
80-
fileReader.OnError = (e) => GetProgress(e, "OnError");
81-
fileReader.OnLoadEnd = (e) =>
84+
85+
await using var eventListener = await EventListenerInProcess<ProgressEventInProcess, ProgressEvent>.CreateAsync(JSRuntime, e =>
8286
{
83-
imageUrl = "data:image/png;base64," + Convert.ToBase64String(fileReader.ResultAsByteArray ?? new byte[0]);
84-
GetProgress(e, "OnLoadEnd");
85-
};
87+
if (e.Type == "loadend")
88+
{
89+
imageUrl = "data:image/png;base64," + Convert.ToBase64String(fileReader.ResultAsByteArray ?? new byte[0]);
90+
}
91+
GetProgress(e);
92+
});
93+
94+
fileReader.AddOnLoadStartEventListener(eventListener);
95+
fileReader.AddOnProgressEventListener(eventListener);
96+
fileReader.AddOnLoadEventListener(eventListener);
97+
fileReader.AddOnAbortEventListener(eventListener);
98+
fileReader.AddOnErrorEventListener(eventListener);
99+
fileReader.AddOnLoadEndEventListener(eventListener);
100+
86101
fileReader.ReadAsArrayBuffer(blob!);
87102
}
88103

89104
public async Task ReadAsBinaryStringAsync()
90105
{
91106
log = "";
92107
var fileReader = await FileReader.CreateAsync(JSRuntime);
93-
fileReader.OnLoadStart = async (e) => await GetProgressAsync(e, "OnLoadStart");
94-
fileReader.OnProgress = async (e) => await GetProgressAsync(e, "OnProgress");
95-
fileReader.OnLoad = async (e) => await GetProgressAsync(e, "OnLoad");
96-
fileReader.OnAbort = async (e) => await GetProgressAsync(e, "OnAbort");
97-
fileReader.OnError = async (e) => await GetProgressAsync(e, "OnError");
98-
fileReader.OnLoadEnd = async (e) =>
108+
109+
await using var eventListener = await EventListener<ProgressEvent>.CreateAsync(JSRuntime, GetProgressAsync);
110+
await using var loadEndEventLister = await EventListener<ProgressEvent>.CreateAsync(JSRuntime, async e =>
99111
{
100112
var bytes = (await fileReader.GetResultAsStringAsync() ?? "").Select(c => (byte)c).ToArray();
101113
imageUrl = "data:image/png;base64," + Convert.ToBase64String(bytes);
102-
await GetProgressAsync(e, "OnLoadEnd");
103-
};
114+
await GetProgressAsync(e);
115+
});
116+
117+
await fileReader.AddOnLoadStartEventListenerAsync(eventListener);
118+
await fileReader.AddOnProgressEventListenerAsync(eventListener);
119+
await fileReader.AddOnLoadEventListenerAsync(eventListener);
120+
await fileReader.AddOnAbortEventListenerAsync(eventListener);
121+
await fileReader.AddOnErrorEventListenerAsync(eventListener);
122+
await fileReader.AddOnLoadEndEventListenerAsync(loadEndEventLister);
123+
104124
await fileReader.ReadAsBinaryStringAsync(blob!);
105125
}
106126

@@ -115,16 +135,29 @@ We then use the methods of the <code>FileReader</code> interface to read the <co
115135
{
116136
log = "";
117137
var fileReader = await FileReader.CreateAsync(JSRuntime);
118-
fileReader.OnLoadStart = async (e) => await GetProgressAsync(e, "OnLoadStart");
119-
fileReader.OnProgress = async (e) => await GetProgressAsync(e, "OnProgress");
120-
fileReader.OnLoad = async (e) => await GetProgressAsync(e, "OnLoad");
121-
fileReader.OnAbort = async (e) => await GetProgressAsync(e, "OnAbort");
122-
fileReader.OnError = async (e) => await GetProgressAsync(e, "OnError");
123-
fileReader.OnLoadEnd = async (e) =>
138+
139+
await using var eventListener = await EventListener<ProgressEvent>.CreateAsync(JSRuntime, GetProgressAsync);
140+
await using var loadEndEventLister = await EventListener<ProgressEvent>.CreateAsync(JSRuntime, async e =>
124141
{
125142
imageUrl = await fileReader.GetResultAsStringAsync() ?? "";
126-
await GetProgressAsync(e, "OnLoadEnd");
127-
};
143+
await GetProgressAsync(e);
144+
});
145+
146+
await fileReader.AddOnLoadStartEventListenerAsync(eventListener);
147+
await fileReader.AddOnProgressEventListenerAsync(eventListener);
148+
await fileReader.AddOnLoadEventListenerAsync(eventListener);
149+
await fileReader.AddOnAbortEventListenerAsync(eventListener);
150+
await fileReader.AddOnErrorEventListenerAsync(eventListener);
151+
await fileReader.AddOnLoadEndEventListenerAsync(loadEndEventLister);
152+
128153
await fileReader.ReadAsDataURLAsync(blob!);
129154
}
155+
156+
public async ValueTask DisposeAsync()
157+
{
158+
if (blob is not null)
159+
{
160+
await blob.DisposeAsync();
161+
}
162+
}
130163
}

samples/KristofferStrube.Blazor.FileAPI.WasmExample/Pages/Index.razor

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
@page "/"
2-
@implements IDisposable
2+
@implements IAsyncDisposable
33

44
@inject IJSRuntime JSRuntime
55
@inject HttpClient HttpClient
@@ -38,8 +38,15 @@ content type: @file?.Type
3838
blobURL = URL.CreateObjectURL(file);
3939
}
4040

41-
public void Dispose()
41+
public async ValueTask DisposeAsync()
4242
{
43-
URL.RevokeObjectURL(blobURL);
43+
if (file is not null)
44+
{
45+
await file.DisposeAsync();
46+
}
47+
if (blobURL is not "")
48+
{
49+
URL.RevokeObjectURL(blobURL);
50+
}
4451
}
4552
}

0 commit comments

Comments
 (0)