mirror of
https://github.com/mollersuite/monofile.git
synced 2024-11-28 16:36:27 -08:00
RANGE HEADER 🎉 I AM SO UNBELIEVABLY HAPPY
This commit is contained in:
parent
9bb5babd1d
commit
72efec9f6d
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue