RANGE HEADER 🎉 I AM SO UNBELIEVABLY HAPPY

This commit is contained in:
May 2023-05-19 22:50:56 -07:00
parent 9bb5babd1d
commit 72efec9f6d
2 changed files with 107 additions and 36 deletions

View file

@ -15,6 +15,7 @@ import * as Accounts from "./lib/accounts"
import { authRoutes, auth_setFilesObj } from "./routes/authRoutes"; import { authRoutes, auth_setFilesObj } from "./routes/authRoutes";
import { fileApiRoutes, setFilesObj } from "./routes/fileApiRoutes"; import { fileApiRoutes, setFilesObj } from "./routes/fileApiRoutes";
import { adminRoutes, admin_setFilesObj } from "./routes/adminRoutes"; import { adminRoutes, admin_setFilesObj } from "./routes/adminRoutes";
import { Range } from "range-parser";
require("dotenv").config() require("dotenv").config()
@ -187,36 +188,46 @@ let fgRQH = async (req:express.Request,res:express.Response) => {
return return
} }
// todo: make readfilestream just the stream since we already have filepointer let range: Range | undefined
files.readFileStream(req.params.fileId).then(async f => {
res.setHeader("Content-Type",f.contentType) res.setHeader("Content-Type",file.mime)
if (f.byteSize) { if (file.sizeInBytes) {
res.setHeader("Content-Length",f.byteSize) res.setHeader("Content-Length",file.sizeInBytes)
if (file.chunkSize) {
let rng = req.range(file.sizeInBytes)
if (rng) {
// error handling
if (typeof rng == "number") {
res.status(rng == -1 ? 416 : 400).send()
return
}
if (rng.type != "bytes") {
res.status(400).send();
return
} }
if (f.byteSize && req.range(f.byteSize)) { // set ranges var
// range header implementation let rngs = Array.from(rng)
// todo : make this better (or actually work if i dont manage to finish it) if (rngs.length != 1) { res.status(400).send(); return }
let ranges = req.range(f.byteSize) range = rngs[0]
if (typeof ranges == "number" || !ranges) { res.status(400); res.send(); return }
let fsds = f.dataStream;
res.status(206);
let bytePosition = 0
console.log(ranges.type)
for await(let x of fsds) {
let curRanges = [ bytePosition, bytePosition+x.byteLength()-1 ]
bytePosition+= x.byteLength
} }
} else { }
res.status(200)
f.dataStream.pipe(res)
} }
// supports ranges
files.readFileStream(req.params.fileId, range).then(async stream => {
if (range) {
res.status(206)
res.header("Content-Length", (range.end-range.start + 1).toString())
res.header("Content-Range", `bytes ${range.start}-${range.end}/${file.sizeInBytes}`)
}
stream.pipe(res)
}).catch((err) => { }).catch((err) => {
ServeError(res,err.status,err.message) ServeError(res,err.status,err.message)
@ -229,6 +240,23 @@ let fgRQH = async (req:express.Request,res:express.Response) => {
} }
let fgwh = (req: express.Request, res:express.Response) => {
let file = files.getFilePointer(req.params.fileId)
if (!file) {
res.status(404)
res.send()
} else {
res.setHeader("Content-Type",file.mime)
if (file.sizeInBytes) {
res.setHeader("Content-Length",file.sizeInBytes)
}
if (file.chunkSize) {
res.setHeader("Accept-Ranges", "bytes")
}
}
}
app.get("/server",(req,res) => { app.get("/server",(req,res) => {
res.send(JSON.stringify({ res.send(JSON.stringify({
...config, ...config,
@ -240,6 +268,9 @@ app.get("/server",(req,res) => {
app.get("/file/:fileId",fgRQH) app.get("/file/:fileId",fgRQH)
app.get("/:fileId",fgRQH) app.get("/:fileId",fgRQH)
app.head("/file/:fileId",fgwh)
app.head("/:fileId",fgwh)
/* /*
routes should be in this order: routes should be in this order:

View file

@ -50,7 +50,8 @@ export interface FilePointer {
sizeInBytes?:number, sizeInBytes?:number,
tag?:string, tag?:string,
visibility?:FileVisibility, visibility?:FileVisibility,
reserved?: boolean reserved?: boolean,
chunkSize?: number
} }
export interface StatusCodeError { export interface StatusCodeError {
@ -147,7 +148,9 @@ export default class Files {
owner:settings.owner, owner:settings.owner,
visibility: settings.owner ? "private" : "public", visibility: settings.owner ? "private" : "public",
reserved: true reserved: true,
chunkSize: this.config.maxDiscordFileSize
} }
// save // save
@ -228,7 +231,9 @@ export default class Files {
: undefined : undefined
), ),
// so that json.stringify doesnt include tag:undefined // so that json.stringify doesnt include tag:undefined
...((ogf||{}).tag ? {tag:ogf.tag} : {}) ...((ogf||{}).tag ? {tag:ogf.tag} : {}),
chunkSize: this.config.maxDiscordFileSize
} }
)) ))
@ -260,7 +265,7 @@ export default class Files {
// todo: move read code here // todo: move read code here
readFileStream(uploadId: string):Promise<{dataStream:Readable,contentType:string,byteSize?:number}> { readFileStream(uploadId: string, range?: {start:number, end:number}):Promise<Readable> {
return new Promise(async (resolve,reject) => { return new Promise(async (resolve,reject) => {
if (!this.uploadChannel) { if (!this.uploadChannel) {
reject({status:503,message:"server is not ready - please try again later"}) reject({status:503,message:"server is not ready - please try again later"})
@ -274,18 +279,50 @@ export default class Files {
read(){} read(){}
}) })
resolve({ resolve(dataStream)
contentType: file.mime,
dataStream: dataStream,
byteSize: file.sizeInBytes
})
for (let i = 0; i < file.messageids.length; i++) { let
let msg = await this.uploadChannel.messages.fetch(file.messageids[i]).catch(() => {return null}) scan_msg_begin = 0,
scan_msg_end = file.messageids.length,
scan_files_begin = 0,
scan_files_end = -1
let useRanges = range && file.chunkSize && file.sizeInBytes;
// todo: figure out how to get typesccript to accept useRanges
// i'm too tired to look it up or write whatever it wnats me to do
if (range && file.chunkSize && file.sizeInBytes) {
// Calculate where to start file scans...
scan_files_begin = Math.floor(range.start / file.chunkSize)
scan_files_end = Math.ceil(range.end / file.chunkSize) - 1
scan_msg_begin = Math.floor(scan_files_begin / 10)
scan_msg_end = Math.ceil(scan_files_end / 10)
}
for (let xi = scan_msg_begin; xi < scan_msg_end; xi++) {
let msg = await this.uploadChannel.messages.fetch(file.messageids[xi]).catch(() => {return null})
if (msg?.attachments) { if (msg?.attachments) {
let attach = Array.from(msg.attachments.values()) let attach = Array.from(msg.attachments.values())
for (let i = 0; i < attach.length; i++) { for (let i = (useRanges && xi == scan_msg_begin ? scan_files_begin : 0); i < (useRanges && xi == scan_msg_end ? scan_files_end : attach.length); i++) {
let d = await axios.get(attach[i].url,{responseType:"arraybuffer"}).catch((e:Error) => {console.error(e)})
let d = await axios.get(
attach[i].url,
{
responseType:"arraybuffer",
headers: {
...(useRanges ? {
"Range": `bytes=${i+(xi*10) == scan_files_begin && range && file.chunkSize ? range.start-(scan_files_begin*file.chunkSize) : "0"}-${i+(xi*10) == scan_files_end && range && file.chunkSize ? range.end-(scan_files_end*file.chunkSize) : ""}`
} : {})
}
}
).catch((e:Error) => {console.error(e)})
if (d) { if (d) {
dataStream.push(d.data) dataStream.push(d.data)
} else { } else {
@ -293,8 +330,11 @@ export default class Files {
dataStream.destroy(new Error("file read error")) dataStream.destroy(new Error("file read error"))
return return
} }
} }
} }
} }
dataStream.push(null) dataStream.push(null)