Use streaming uploads in chrome 105

This commit is contained in:
Kieran 2022-09-09 13:22:53 +01:00
parent 9c4b7bd820
commit ee8b915797
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
7 changed files with 94 additions and 30 deletions

View File

@ -87,12 +87,13 @@ namespace VoidCat.Controllers
Name = filename, Name = filename,
Description = Request.Headers.GetHeader("V-Description"), Description = Request.Headers.GetHeader("V-Description"),
Digest = Request.Headers.GetHeader("V-Full-Digest"), Digest = Request.Headers.GetHeader("V-Full-Digest"),
Size = (ulong?)Request.ContentLength ?? 0UL, Size = (ulong?) Request.ContentLength ?? 0UL,
Storage = store Storage = store
}; };
var (segment, totalSegments) = ParseSegmentsHeader(); var (segment, totalSegments) = ParseSegmentsHeader();
var vf = await _storage.Ingress(new(Request.Body, meta, segment, totalSegments), HttpContext.RequestAborted); var vf = await _storage.Ingress(new(Request.Body, meta, segment, totalSegments),
HttpContext.RequestAborted);
// save metadata // save metadata
await _metadata.Set(vf.Id, vf.Metadata!); await _metadata.Set(vf.Id, vf.Metadata!);
@ -343,7 +344,7 @@ namespace VoidCat.Controllers
public Guid EditSecret { get; init; } public Guid EditSecret { get; init; }
public StrikePaymentConfig? Strike { get; init; } public StrikePaymentConfig? Strike { get; init; }
public bool Required { get; init; } public bool Required { get; init; }
} }
} }

View File

@ -122,6 +122,7 @@ public static class VoidStartup
{ {
p.AllowAnyMethod() p.AllowAnyMethod()
.AllowAnyHeader() .AllowAnyHeader()
.AllowCredentials()
.WithOrigins(voidSettings.CorsOrigins.Select(a => a.OriginalString).ToArray()); .WithOrigins(voidSettings.CorsOrigins.Select(a => a.OriginalString).ToArray());
}); });
}); });

View File

@ -8,6 +8,7 @@
"@reduxjs/toolkit": "^1.7.2", "@reduxjs/toolkit": "^1.7.2",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"feather-icons-react": "^0.5.0", "feather-icons-react": "^0.5.0",
"http-proxy-middleware": "^2.0.6",
"moment": "^2.29.4", "moment": "^2.29.4",
"preval.macro": "^5.0.0", "preval.macro": "^5.0.0",
"qrcode.react": "^1.0.1", "qrcode.react": "^1.0.1",

View File

@ -40,45 +40,66 @@ export function FileUpload(props) {
} }
async function doStreamUpload() { async function doStreamUpload() {
setUState(UploadState.Hashing);
let hash = await digest(props.file);
calc.Reset();
let offset = 0; let offset = 0;
async function readChunk(size) {
if (offset > props.file.size) {
return new Uint8Array(0);
}
let end = Math.min(offset + size, props.file.size);
let blob = props.file.slice(offset, end, props.file.type);
let data = await blob.arrayBuffer();
offset += data.byteLength;
return new Uint8Array(data);
}
let rs = new ReadableStream({ let rs = new ReadableStream({
start: (controller) => { start: (controller) => {
console.log(controller);
setUState(UploadState.Uploading);
}, },
pull: async (controller) => { pull: async (controller) => {
if (offset > props.file.size) { console.log(controller);
controller.cancel(); let chunk = await readChunk(controller.desiredSize);
if (chunk.byteLength === 0) {
controller.close();
return;
} }
let requestedSize = props.file.size / controller.desiredSize; calc.ReportLoaded(chunk.byteLength);
console.log(`Reading ${requestedSize} Bytes`); setSpeed(calc.RateWindow(5));
setProgress(offset / props.file.size);
let end = Math.min(offset + requestedSize, props.file.size);
let blob = props.file.slice(offset, end, props.file.type); controller.enqueue(chunk);
controller.enqueue(await blob.arrayBuffer());
offset += blob.size;
}, },
cancel: (reason) => { cancel: (reason) => {
console.log(reason);
} },
type: "bytes"
}, { }, {
highWaterMark: 100 highWaterMark: 1024 * 1024
}); });
let req = await fetch("/upload", { let req = await fetch("https://localhost:7195/upload", {
method: "POST", method: "POST",
mode: "cors",
body: rs, body: rs,
headers: { headers: {
"Content-Type": "application/octet-stream", "Content-Type": "application/octet-stream",
"V-Content-Type": props.file.type, "V-Content-Type": props.file.type,
"V-Filename": props.file.name "V-Filename": props.file.name,
} "V-Full-Digest": hash
},
duplex: 'half'
}); });
if (req.ok) { if (req.ok) {
let rsp = await req.json(); let rsp = await req.json();
console.log(rsp); console.log(rsp);
setResult(rsp); handleResult(rsp);
} }
} }
@ -143,7 +164,7 @@ export function FileUpload(props) {
await doSplitXHRUpload(hash, uploadSize); await doSplitXHRUpload(hash, uploadSize);
} else { } else {
let xhr = await xhrSegment(props.file, hash); let xhr = await xhrSegment(props.file, hash);
handleXHRResult(xhr); handleResult(xhr);
} }
} }
@ -160,20 +181,25 @@ export function FileUpload(props) {
break; break;
} }
} }
handleXHRResult(xhr); handleResult(xhr);
} }
function handleXHRResult(xhr) { function handleResult(result) {
if (xhr.ok) { if (result.ok) {
setUState(UploadState.Done); setUState(UploadState.Done);
setResult(xhr.file); setResult(result.file);
window.localStorage.setItem(xhr.file.id, JSON.stringify(xhr.file)); window.localStorage.setItem(result.file.id, JSON.stringify(result.file));
} else { } else {
setUState(UploadState.Failed); setUState(UploadState.Failed);
setResult(xhr.errorMessage); setResult(result.errorMessage);
} }
} }
function getChromeVersion () {
let raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
return raw ? parseInt(raw[2], 10) : false;
}
async function digest(file) { async function digest(file) {
const chunkSize = 100_000_000; const chunkSize = 100_000_000;
let sha = CryptoJS.algo.SHA256.create(); let sha = CryptoJS.algo.SHA256.create();
@ -220,7 +246,13 @@ export function FileUpload(props) {
useEffect(() => { useEffect(() => {
console.log(props.file); console.log(props.file);
doXHRUpload().catch(console.error);
let chromeVersion = getChromeVersion();
if(chromeVersion >= 105) {
doStreamUpload().catch(console.error);
} else {
doXHRUpload().catch(console.error);
}
}, []); }, []);
return ( return (

View File

@ -16,7 +16,7 @@ export function FooterLinks() {
<a href="https://github.com/v0l/void.cat" target="_blank"> <a href="https://github.com/v0l/void.cat" target="_blank">
GitHub GitHub
</a> </a>
{profile ? <a href="/admin">Admin</a> : null} {profile?.roles?.includes("Admin") ? <a href="/admin">Admin</a> : null}
</div> </div>
); );
} }

View File

@ -0,0 +1,18 @@
const {createProxyMiddleware} = require('http-proxy-middleware');
const settings = require("../package.json");
module.exports = function (app) {
const proxy = createProxyMiddleware({
target: settings.proxy,
changeOrigin: true,
secure: false
});
app.use('/admin', proxy);
app.use('/d', proxy);
app.use('/info', proxy);
app.use('/upload', proxy);
app.use('/auth', proxy);
app.use('/swagger', proxy);
app.use('/user', proxy);
};

View File

@ -4626,6 +4626,17 @@ http-proxy-middleware@^2.0.0:
is-plain-obj "^3.0.0" is-plain-obj "^3.0.0"
micromatch "^4.0.2" micromatch "^4.0.2"
http-proxy-middleware@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f"
integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==
dependencies:
"@types/http-proxy" "^1.17.8"
http-proxy "^1.18.1"
is-glob "^4.0.1"
is-plain-obj "^3.0.0"
micromatch "^4.0.2"
http-proxy@^1.18.1: http-proxy@^1.18.1:
version "1.18.1" version "1.18.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"