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

Commit

Permalink
Support conversion/resizing of image files
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveSandersonMS committed Apr 28, 2020
1 parent ef155e5 commit db43dc1
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 2 deletions.
6 changes: 6 additions & 0 deletions BlazorInputFile/FileListEntryImpl.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Threading.Tasks;

namespace BlazorInputFile
{
Expand Down Expand Up @@ -34,6 +35,11 @@ public Stream Data
}
}

public async Task<IFileListEntry> ToImageFileAsync(string format, int maxWidth, int maxHeight)
{
return await Owner.ConvertToImageFileAsync(this, format, maxWidth, maxHeight);
}

internal void RaiseOnDataRead()
{
OnDataRead?.Invoke(this, null);
Expand Down
3 changes: 3 additions & 0 deletions BlazorInputFile/IFileListEntry.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Threading.Tasks;

namespace BlazorInputFile
{
Expand All @@ -17,6 +18,8 @@ public interface IFileListEntry

Stream Data { get; }

Task<IFileListEntry> ToImageFileAsync(string format, int maxWidth, int maxHeight);

event EventHandler OnDataRead;
}
}
10 changes: 10 additions & 0 deletions BlazorInputFile/InputFile.razor
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@
: new RemoteFileListEntryStream(JSRuntime, inputFileElement, file, MaxMessageSize, MaxBufferSize);
}

internal async Task<FileListEntryImpl> ConvertToImageFileAsync(FileListEntryImpl file, string format, int maxWidth, int maxHeight)
{
var imageFile = await JSRuntime.InvokeAsync<FileListEntryImpl>("BlazorInputFile.toImageFile", inputFileElement, file.Id, format, maxWidth, maxHeight);

// So that method invocations on the file can be dispatched back here
imageFile.Owner = (InputFile)(object)this;

return imageFile;
}

void IDisposable.Dispose()
{
thisReference?.Dispose();
Expand Down
39 changes: 37 additions & 2 deletions BlazorInputFile/wwwroot/inputfile.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
(function () {
window.BlazorInputFile = {
init: function init(elem, componentInstance) {
var nextFileId = 0;
elem._blazorInputFileNextFileId = 0;

elem.addEventListener('change', function handleInputFileChange(event) {
// Reduce to purely serializable data, plus build an index by ID
elem._blazorFilesById = {};
var fileList = Array.prototype.map.call(elem.files, function (file) {
var result = {
id: ++nextFileId,
id: ++elem._blazorInputFileNextFileId,
lastModified: new Date(file.lastModified).toISOString(),
name: file.name,
size: file.size,
Expand All @@ -34,6 +34,41 @@
});
},

toImageFile(elem, fileId, format, maxWidth, maxHeight) {
var originalFile = getFileById(elem, fileId);
return createImageBitmap(originalFile.blob)
.then(function (imageBitmap) {
return new Promise(function (resolve) {
var desiredWidthRatio = Math.min(1, maxWidth / imageBitmap.width);
var desiredHeightRatio = Math.min(1, maxHeight / imageBitmap.height);
var chosenSizeRatio = Math.min(desiredWidthRatio, desiredHeightRatio);

var canvas = document.createElement('canvas');
canvas.width = Math.round(imageBitmap.width * chosenSizeRatio);
canvas.height = Math.round(imageBitmap.height * chosenSizeRatio);
canvas.getContext('2d').drawImage(imageBitmap, 0, 0, canvas.width, canvas.height);
return canvas.toBlob(resolve, format);
});
})
.then(function (resizedImageBlob) {
var result = {
id: ++elem._blazorInputFileNextFileId,
lastModified: originalFile.lastModified,
name: originalFile.name, // Note: we're not changing the file extension
size: resizedImageBlob.size,
type: format,
relativePath: originalFile.relativePath
};

elem._blazorFilesById[result.id] = result;

// Attach the blob data itself as a non-enumerable property so it doesn't appear in the JSON
Object.defineProperty(result, 'blob', { value: resizedImageBlob });

return result;
});
},

readFileData: function readFileData(elem, fileId, startOffset, count) {
var readPromise = getArrayBufferFromFileAsync(elem, fileId);

Expand Down
1 change: 1 addition & 0 deletions samples/Sample.Core/App.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<NavLink href="" Match="NavLinkMatch.All">Single file</NavLink>
<NavLink href="multi">Multiple files</NavLink>
<NavLink href="dragdrop-viewer">Drag/drop viewer</NavLink>
<NavLink href="image">Image file</NavLink>
<NavLink href="native">Native upload</NavLink>
</nav>
<main>
Expand Down
37 changes: 37 additions & 0 deletions samples/Sample.Core/Pages/ImageFile.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@page "/image"

<h1>Image file</h1>

<p>A single file input that reads the input as an image in a chosen format with specified maximum dimensions.</p>

<InputFile OnChange="HandleSelection" />

<p>@status</p>

@if (!string.IsNullOrEmpty(imageDataUri))
{
<img src="@imageDataUri" />
}

@code {
string status;
string imageDataUri;

async Task HandleSelection(IFileListEntry[] files)
{
var rawFile = files.FirstOrDefault();
if (rawFile != null)
{
// Load as an image file in memory
var format = "image/jpeg";
var imageFile = await rawFile.ToImageFileAsync(format, 640, 480);
var ms = new MemoryStream();
await imageFile.Data.CopyToAsync(ms);

// Make a data URL so we can display it
imageDataUri = $"data:{format};base64,{Convert.ToBase64String(ms.ToArray())}";

status = $"Finished loading {ms.Length} bytes from {imageFile.Name}";
}
}
}

0 comments on commit db43dc1

Please sign in to comment.