-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d63d758
commit 3fb65c1
Showing
6 changed files
with
210 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
using Microsoft.AspNetCore.Components; | ||
using Microsoft.JSInterop; | ||
|
||
namespace ArcaneLibs.Blazor.Components; | ||
|
||
public static class StreamExtensions { | ||
private static IJSObjectReference? _streamExtensionsModule; | ||
|
||
// public static async Task<string> GetBlobUriAsync(this IJSRuntime jsRuntime, Stream stream, bool revokeAfterFinish = true, int revocationDelay = 1000) { | ||
// if (_streamExtensionsModule is null) { | ||
// Console.WriteLine("Importing stream extensions module"); | ||
// _streamExtensionsModule = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./_content/ArcaneLibs.Blazor.Components/streamExtensions.js"); | ||
// } | ||
// | ||
// Console.WriteLine($"Blob stream {stream.GetHashCode()} position {stream.Position} length {stream.Length}"); | ||
// if (revokeAfterFinish) { | ||
// Task.Run(async () => { | ||
// var isDisposed = false; | ||
// long lastPosition = 0; | ||
// while (!isDisposed) { | ||
// try { | ||
// await Task.Delay(revocationDelay); | ||
// Console.WriteLine($"Blob stream {stream.GetHashCode()} position {stream.Position} length {stream.Length}"); | ||
// } | ||
// catch (ObjectDisposedException) { | ||
// isDisposed = true; | ||
// } | ||
// } | ||
// | ||
// Console.WriteLine($"Blob stream {stream.GetHashCode()} disposed, revoking after {lastPosition} bytes!"); | ||
// await _streamExtensionsModule.InvokeVoidAsync("revokeBlobUri", stream); | ||
// }); | ||
// } | ||
// | ||
// using var streamRef = new DotNetStreamReference(stream, true); | ||
// Console.WriteLine("got streamRef"); | ||
// return await _streamExtensionsModule.InvokeAsync<string>("getBlobUri", streamRef); | ||
// } | ||
|
||
|
||
public static async Task<string> streamImage(this IJSRuntime jsRuntime, Stream stream, ElementReference element, bool revokeAfterFinish = false, int revocationDelay = 1000) { | ||
if (_streamExtensionsModule is null) { | ||
Console.WriteLine("Importing stream extensions module"); | ||
_streamExtensionsModule = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./_content/ArcaneLibs.Blazor.Components/streamExtensions.js"); | ||
Console.WriteLine("Imported stream extensions module"); | ||
} | ||
|
||
Console.WriteLine($"Blob stream {stream.GetHashCode()}"); | ||
if (revokeAfterFinish) { | ||
Task.Run(async () => { | ||
Check warning on line 50 in ArcaneLibs.Blazor.Components/StreamExtensions.cs GitHub Actions / build
|
||
var isDisposed = false; | ||
long lastPosition = 0; | ||
while (!isDisposed) { | ||
try { | ||
await Task.Delay(revocationDelay); | ||
Console.WriteLine($"Blob stream {stream.GetHashCode()} position {stream.Position} length {stream.Length}"); | ||
} | ||
catch (ObjectDisposedException) { | ||
isDisposed = true; | ||
} | ||
} | ||
Console.WriteLine($"Blob stream {stream.GetHashCode()} disposed, revoking after {lastPosition} bytes!"); | ||
await _streamExtensionsModule.InvokeVoidAsync("revokeBlobUri", stream); | ||
}); | ||
} | ||
|
||
using var streamRef = new DotNetStreamReference(stream, true); | ||
Console.WriteLine("got streamRef"); | ||
return await _streamExtensionsModule.InvokeAsync<string>("streamImage", streamRef, element); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
@using Microsoft.JSInterop | ||
@inject IJSRuntime JSRuntime | ||
<img src="@_finalBlob" srcref="@(_self)" @attributes="@(OtherAttributes!)"/> @* @ref="Self" *@ | ||
|
||
@code { | ||
private static IJSObjectReference? _streamExtensionsModule; | ||
private static SemaphoreSlim _importSemaphore = new(1, 1); | ||
private static SemaphoreSlim _streamSemaphore = new(16, 16); | ||
private string _self = Guid.NewGuid().ToString(); | ||
|
||
private Stream? _stream; | ||
|
||
// private ElementReference Self { | ||
// get => _self; | ||
// set { | ||
// Console.WriteLine($"StreamedImage: {_self.Id} -> {value.Id}"); | ||
// _self = value; | ||
@* } *@ | ||
@* } *@ | ||
|
||
private string? _finalBlob; | ||
private CancellationTokenSource _cts = new(); | ||
// private ElementReference _self; | ||
[Parameter(CaptureUnmatchedValues = true)] | ||
public Dictionary<string, object> OtherAttributes { get; set; } = []; | ||
|
||
[Parameter] | ||
public Stream? Stream { | ||
get => _stream; | ||
set { | ||
if (_stream == value) return; | ||
_stream = value; | ||
_cts.Cancel(); | ||
_cts = new(); | ||
_ = StreamHasChanged(value, _cts.Token); | ||
} | ||
} | ||
|
||
protected override async Task OnInitializedAsync() { } | ||
Check warning on line 40 in ArcaneLibs.Blazor.Components/StreamedImage.razor GitHub Actions / build
Check warning on line 40 in ArcaneLibs.Blazor.Components/StreamedImage.razor GitHub Actions / build
|
||
|
||
private async Task StreamHasChanged(Stream? stream, CancellationToken? cancellationToken) { | ||
if (stream is null) return; | ||
await _importSemaphore.WaitAsync(); | ||
if (_streamExtensionsModule is null) { | ||
Console.WriteLine("Importing StreamedImage JS module"); | ||
_streamExtensionsModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./_content/ArcaneLibs.Blazor.Components/StreamedImage.razor.js"); | ||
Console.WriteLine("Successfully imported StreamedImage JS module"); | ||
} | ||
_importSemaphore.Release(); | ||
|
||
StateHasChanged(); | ||
|
||
using var streamRef = new DotNetStreamReference(stream, true); | ||
// Console.WriteLine($"StreamedImage: got streamRef {streamRef.GetHashCode()} for stream {stream.GetHashCode()}"); | ||
await _streamSemaphore.WaitAsync(); | ||
Console.WriteLine($"StreamedImage: Attempting to invoke streamImage({streamRef}, {_self})"); | ||
var finalBlob = await _streamExtensionsModule.InvokeAsync<string>("streamImage", streamRef, _self); | ||
_streamSemaphore.Release(); | ||
// Console.WriteLine("StreamedImage.razor: got finalBlob " + finalBlob); | ||
// var finalBlob = await JSRuntime.streamImage(Stream, Self); | ||
if (cancellationToken is { IsCancellationRequested: true }) return; | ||
_finalBlob = finalBlob; | ||
StateHasChanged(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
export async function streamImage(stream, srcRef) { | ||
console.log("streamImage start"); | ||
const actualStream = await stream.stream(); | ||
const reader = actualStream.getReader(); | ||
|
||
console.log(`StreamedImage: streamImage started for srcRef: ${srcRef}`); | ||
|
||
const chunks = []; | ||
while (true) { | ||
const {done, value} = await reader.read(); | ||
chunks.push(value); | ||
|
||
if (chunks.length <= 10 || chunks.length % 10 === 0 || done) { | ||
//srcRef attribute on img tag | ||
let imageElement = document.querySelector(`img[srcref="${srcRef}"]`); | ||
console.log('meow', srcRef, imageElement); | ||
// console.log(`StreamedImage: streamImage done: ${done}, value: ${value?.length}, chunks: ${chunks.length}`); | ||
let blob = new Blob(chunks); | ||
const url = window.URL.createObjectURL(blob); | ||
imageElement.src = url; | ||
// console.log(`StreamedImage: streamImage src: ${url} - done: ${done}`) | ||
|
||
if (done) { | ||
console.log(`StreamedImage.razor.js: streamImage finished in chunks: ${chunks.length} -> ${url}`); | ||
return url; | ||
} else { | ||
// Dispose old URLs to avoid memory leaks | ||
setTimeout(() => { | ||
window.URL.revokeObjectURL(url); | ||
}, 100); | ||
} | ||
} | ||
|
||
await new Promise(resolve => setTimeout(resolve, chunks.length * 5)); | ||
|
||
//yield to other tasks | ||
// if(chunks.length > 100) { | ||
// let delay = Math.min(2000, chunks.length * 10); | ||
// console.warn(`StreamedImage: streamImage waiting ${delay} for chunks: ${chunks.length}, last read length: ${value?.length}`); | ||
// await new Promise(resolve => setTimeout(resolve, delay)); | ||
// } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
export async function streamImage(stream, imageElement) { | ||
const actualStream = await stream.stream(); | ||
const reader = actualStream.getReader(); | ||
|
||
const chunks = []; | ||
let blob = null; | ||
while (true) { | ||
const {done, value} = await reader.read(); | ||
console.log(`streamImage done: ${done}, value: ${value?.length}, chunks: ${chunks.length}`); | ||
chunks.push(value); | ||
|
||
if (blob !== null) { | ||
window.URL.revokeObjectURL(blob); | ||
} | ||
blob = new Blob(chunks, {type: 'image/jpeg'}); | ||
const url = window.URL.createObjectURL(blob); | ||
imageElement.src = url; | ||
setTimeout(() => { | ||
window.URL.revokeObjectURL(url); | ||
}, 100); | ||
if (done) { | ||
break; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters