Skip to content
This repository was archived by the owner on May 5, 2021. It is now read-only.

Commit db43dc1

Browse files
Support conversion/resizing of image files
1 parent ef155e5 commit db43dc1

File tree

6 files changed

+94
-2
lines changed

6 files changed

+94
-2
lines changed

BlazorInputFile/FileListEntryImpl.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.IO;
3+
using System.Threading.Tasks;
34

45
namespace BlazorInputFile
56
{
@@ -34,6 +35,11 @@ public Stream Data
3435
}
3536
}
3637

38+
public async Task<IFileListEntry> ToImageFileAsync(string format, int maxWidth, int maxHeight)
39+
{
40+
return await Owner.ConvertToImageFileAsync(this, format, maxWidth, maxHeight);
41+
}
42+
3743
internal void RaiseOnDataRead()
3844
{
3945
OnDataRead?.Invoke(this, null);

BlazorInputFile/IFileListEntry.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.IO;
3+
using System.Threading.Tasks;
34

45
namespace BlazorInputFile
56
{
@@ -17,6 +18,8 @@ public interface IFileListEntry
1718

1819
Stream Data { get; }
1920

21+
Task<IFileListEntry> ToImageFileAsync(string format, int maxWidth, int maxHeight);
22+
2023
event EventHandler OnDataRead;
2124
}
2225
}

BlazorInputFile/InputFile.razor

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@
4040
: new RemoteFileListEntryStream(JSRuntime, inputFileElement, file, MaxMessageSize, MaxBufferSize);
4141
}
4242

43+
internal async Task<FileListEntryImpl> ConvertToImageFileAsync(FileListEntryImpl file, string format, int maxWidth, int maxHeight)
44+
{
45+
var imageFile = await JSRuntime.InvokeAsync<FileListEntryImpl>("BlazorInputFile.toImageFile", inputFileElement, file.Id, format, maxWidth, maxHeight);
46+
47+
// So that method invocations on the file can be dispatched back here
48+
imageFile.Owner = (InputFile)(object)this;
49+
50+
return imageFile;
51+
}
52+
4353
void IDisposable.Dispose()
4454
{
4555
thisReference?.Dispose();

BlazorInputFile/wwwroot/inputfile.js

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
(function () {
22
window.BlazorInputFile = {
33
init: function init(elem, componentInstance) {
4-
var nextFileId = 0;
4+
elem._blazorInputFileNextFileId = 0;
55

66
elem.addEventListener('change', function handleInputFileChange(event) {
77
// Reduce to purely serializable data, plus build an index by ID
88
elem._blazorFilesById = {};
99
var fileList = Array.prototype.map.call(elem.files, function (file) {
1010
var result = {
11-
id: ++nextFileId,
11+
id: ++elem._blazorInputFileNextFileId,
1212
lastModified: new Date(file.lastModified).toISOString(),
1313
name: file.name,
1414
size: file.size,
@@ -34,6 +34,41 @@
3434
});
3535
},
3636

37+
toImageFile(elem, fileId, format, maxWidth, maxHeight) {
38+
var originalFile = getFileById(elem, fileId);
39+
return createImageBitmap(originalFile.blob)
40+
.then(function (imageBitmap) {
41+
return new Promise(function (resolve) {
42+
var desiredWidthRatio = Math.min(1, maxWidth / imageBitmap.width);
43+
var desiredHeightRatio = Math.min(1, maxHeight / imageBitmap.height);
44+
var chosenSizeRatio = Math.min(desiredWidthRatio, desiredHeightRatio);
45+
46+
var canvas = document.createElement('canvas');
47+
canvas.width = Math.round(imageBitmap.width * chosenSizeRatio);
48+
canvas.height = Math.round(imageBitmap.height * chosenSizeRatio);
49+
canvas.getContext('2d').drawImage(imageBitmap, 0, 0, canvas.width, canvas.height);
50+
return canvas.toBlob(resolve, format);
51+
});
52+
})
53+
.then(function (resizedImageBlob) {
54+
var result = {
55+
id: ++elem._blazorInputFileNextFileId,
56+
lastModified: originalFile.lastModified,
57+
name: originalFile.name, // Note: we're not changing the file extension
58+
size: resizedImageBlob.size,
59+
type: format,
60+
relativePath: originalFile.relativePath
61+
};
62+
63+
elem._blazorFilesById[result.id] = result;
64+
65+
// Attach the blob data itself as a non-enumerable property so it doesn't appear in the JSON
66+
Object.defineProperty(result, 'blob', { value: resizedImageBlob });
67+
68+
return result;
69+
});
70+
},
71+
3772
readFileData: function readFileData(elem, fileId, startOffset, count) {
3873
var readPromise = getArrayBufferFromFileAsync(elem, fileId);
3974

samples/Sample.Core/App.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<NavLink href="" Match="NavLinkMatch.All">Single file</NavLink>
44
<NavLink href="multi">Multiple files</NavLink>
55
<NavLink href="dragdrop-viewer">Drag/drop viewer</NavLink>
6+
<NavLink href="image">Image file</NavLink>
67
<NavLink href="native">Native upload</NavLink>
78
</nav>
89
<main>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
@page "/image"
2+
3+
<h1>Image file</h1>
4+
5+
<p>A single file input that reads the input as an image in a chosen format with specified maximum dimensions.</p>
6+
7+
<InputFile OnChange="HandleSelection" />
8+
9+
<p>@status</p>
10+
11+
@if (!string.IsNullOrEmpty(imageDataUri))
12+
{
13+
<img src="@imageDataUri" />
14+
}
15+
16+
@code {
17+
string status;
18+
string imageDataUri;
19+
20+
async Task HandleSelection(IFileListEntry[] files)
21+
{
22+
var rawFile = files.FirstOrDefault();
23+
if (rawFile != null)
24+
{
25+
// Load as an image file in memory
26+
var format = "image/jpeg";
27+
var imageFile = await rawFile.ToImageFileAsync(format, 640, 480);
28+
var ms = new MemoryStream();
29+
await imageFile.Data.CopyToAsync(ms);
30+
31+
// Make a data URL so we can display it
32+
imageDataUri = $"data:{format};base64,{Convert.ToBase64String(ms.ToArray())}";
33+
34+
status = $"Finished loading {ms.Length} bytes from {imageFile.Name}";
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)