mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-29 02:52:27 +08:00
chore: convert to LF line ending
This commit is contained in:
parent
d41e2443bc
commit
a3b6d4d151
298
Readme.md
298
Readme.md
@ -1,149 +1,149 @@
|
|||||||

|

|
||||||
|
|
||||||
# About
|
# About
|
||||||
|
|
||||||
A HTTP 206 Partial Content handler to serve any readable stream partially in Express.
|
A HTTP 206 Partial Content handler to serve any readable stream partially in Express.
|
||||||
|
|
||||||
Based on this blog post: https://www.codeproject.com/Articles/813480/HTTP-Partial-Content-In-Node-js.
|
Based on this blog post: https://www.codeproject.com/Articles/813480/HTTP-Partial-Content-In-Node-js.
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
`yarn add express-partial-content`
|
`yarn add express-partial-content`
|
||||||
|
|
||||||
OR
|
OR
|
||||||
|
|
||||||
`npm install express-partial-content`
|
`npm install express-partial-content`
|
||||||
|
|
||||||
> Note: `Express` package is a peer dependency for `express-partial-content` and must be present in dependencies of the host package.
|
> Note: `Express` package is a peer dependency for `express-partial-content` and must be present in dependencies of the host package.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
From the `express-file-server` example:
|
From the `express-file-server` example:
|
||||||
|
|
||||||
1. Implement a `ContentProvider` function which prepares and returns a `Content` object:
|
1. Implement a `ContentProvider` function which prepares and returns a `Content` object:
|
||||||
|
|
||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { Range, ContentDoesNotExistError, ContentProvider } from "express-partial-content";
|
import { Range, ContentDoesNotExistError, ContentProvider } from "express-partial-content";
|
||||||
import {logger} from "./logger";
|
import {logger} from "./logger";
|
||||||
|
|
||||||
const statAsync = promisify(fs.stat);
|
const statAsync = promisify(fs.stat);
|
||||||
const existsAsync = promisify(fs.exists);
|
const existsAsync = promisify(fs.exists);
|
||||||
|
|
||||||
export const fileContentProvider: ContentProvider = async (req: Request) => {
|
export const fileContentProvider: ContentProvider = async (req: Request) => {
|
||||||
// Read file name from route params.
|
// Read file name from route params.
|
||||||
const fileName = req.params.name;
|
const fileName = req.params.name;
|
||||||
const file = `${__dirname}/files/${fileName}`;
|
const file = `${__dirname}/files/${fileName}`;
|
||||||
if (!(await existsAsync(file))) {
|
if (!(await existsAsync(file))) {
|
||||||
throw new ContentDoesNotExistError(`File doesn't exist: ${file}`);
|
throw new ContentDoesNotExistError(`File doesn't exist: ${file}`);
|
||||||
}
|
}
|
||||||
const stats = await statAsync(file);
|
const stats = await statAsync(file);
|
||||||
const totalSize = stats.size;
|
const totalSize = stats.size;
|
||||||
const mimeType = "application/octet-stream";
|
const mimeType = "application/octet-stream";
|
||||||
const getStream = (range?: Range) => {
|
const getStream = (range?: Range) => {
|
||||||
if (!range) {
|
if (!range) {
|
||||||
// Request if for complete content.
|
// Request if for complete content.
|
||||||
return fs.createReadStream(file);
|
return fs.createReadStream(file);
|
||||||
}
|
}
|
||||||
// Partial content request.
|
// Partial content request.
|
||||||
const { start, end } = range;
|
const { start, end } = range;
|
||||||
logger.debug(`start: ${start}, end: ${end}`);
|
logger.debug(`start: ${start}, end: ${end}`);
|
||||||
return fs.createReadStream(file, { start, end });
|
return fs.createReadStream(file, { start, end });
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
fileName,
|
fileName,
|
||||||
totalSize,
|
totalSize,
|
||||||
mimeType,
|
mimeType,
|
||||||
getStream
|
getStream
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
2. In your express code, use `createPartialContentHandler` factory method to generate an express handler for serving partial content for the route of your choice:
|
2. In your express code, use `createPartialContentHandler` factory method to generate an express handler for serving partial content for the route of your choice:
|
||||||
|
|
||||||
import {createPartialContentHandler} from "express-partial-content";
|
import {createPartialContentHandler} from "express-partial-content";
|
||||||
import {logger} from "./logger";
|
import {logger} from "./logger";
|
||||||
|
|
||||||
const handler = createPartialContentHandler(fileContentProvider, logger);
|
const handler = createPartialContentHandler(fileContentProvider, logger);
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = 8080;
|
const port = 8080;
|
||||||
|
|
||||||
// File name is a route param.
|
// File name is a route param.
|
||||||
app.get("/files/:name", handler);
|
app.get("/files/:name", handler);
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
logger.debug("Server started!");
|
logger.debug("Server started!");
|
||||||
});
|
});
|
||||||
|
|
||||||
3. Run your server and use a multi-part/multi-connection download utility like [aria2c](https://aria2.github.io/) to test it:
|
3. Run your server and use a multi-part/multi-connection download utility like [aria2c](https://aria2.github.io/) to test it:
|
||||||
|
|
||||||
aria -x5 -k1M http://localhost:8080/files/readme.txt
|
aria -x5 -k1M http://localhost:8080/files/readme.txt
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
There one examples in the `src/examples` folder:
|
There one examples in the `src/examples` folder:
|
||||||
|
|
||||||
1. `express-file-server`: Implements a file based `ContentProvider`.
|
1. `express-file-server`: Implements a file based `ContentProvider`.
|
||||||
|
|
||||||
## Running the examples:
|
## Running the examples:
|
||||||
|
|
||||||
1. `express-file-server`: Run the following commands, the server will listen on http://localhost:8080/.
|
1. `express-file-server`: Run the following commands, the server will listen on http://localhost:8080/.
|
||||||
|
|
||||||
yarn build:dev
|
yarn build:dev
|
||||||
yarn copy-assets
|
yarn copy-assets
|
||||||
yarn run:examples:file
|
yarn run:examples:file
|
||||||
|
|
||||||
## Connecting to the running server:
|
## Connecting to the running server:
|
||||||
|
|
||||||
Browse to `https://localhost:8080/files/readme.txt`
|
Browse to `https://localhost:8080/files/readme.txt`
|
||||||
|
|
||||||
# Reference
|
# Reference
|
||||||
|
|
||||||
## createPartialContentHandler function:
|
## createPartialContentHandler function:
|
||||||
|
|
||||||
This is a factory method which generates a partial content handler for express routes.
|
This is a factory method which generates a partial content handler for express routes.
|
||||||
|
|
||||||
### Arguments:
|
### Arguments:
|
||||||
|
|
||||||
- `contentProvider`: An `async` function which returns a Promise resolved to a `Content` object (see below).
|
- `contentProvider`: An `async` function which returns a Promise resolved to a `Content` object (see below).
|
||||||
- `logger`: Any logging implementation which has a `debug(message:string, extra: any)` method. Either `winston` or `bunyan` loggers should work.
|
- `logger`: Any logging implementation which has a `debug(message:string, extra: any)` method. Either `winston` or `bunyan` loggers should work.
|
||||||
|
|
||||||
### Returns:
|
### Returns:
|
||||||
|
|
||||||
- Express Route Handler: `createPartialContentHandler` returns an express handler which can be mapped to an Express route to serve partial content.
|
- Express Route Handler: `createPartialContentHandler` returns an express handler which can be mapped to an Express route to serve partial content.
|
||||||
|
|
||||||
## ContentProvider function:
|
## ContentProvider function:
|
||||||
|
|
||||||
This function _needs to be implemented by you_. It's purpose is to fetch and return `Content` object containing necessary metadata and methods to stream the content partially. This method is invoked by the express handler (returned by `createPartialContentHandler`) on each request.
|
This function _needs to be implemented by you_. It's purpose is to fetch and return `Content` object containing necessary metadata and methods to stream the content partially. This method is invoked by the express handler (returned by `createPartialContentHandler`) on each request.
|
||||||
|
|
||||||
### Arguments:
|
### Arguments:
|
||||||
|
|
||||||
- `Request`: It receives the `Request` object as it's only input. Use the information available in `Request` to find the requested content, e.g. through `Request.params` or query string, headers etc.
|
- `Request`: It receives the `Request` object as it's only input. Use the information available in `Request` to find the requested content, e.g. through `Request.params` or query string, headers etc.
|
||||||
|
|
||||||
### Returns:
|
### Returns:
|
||||||
|
|
||||||
- `Promise<Content>`: See below.
|
- `Promise<Content>`: See below.
|
||||||
|
|
||||||
### Throws:
|
### Throws:
|
||||||
|
|
||||||
- `ContentDoesNotExistError`: Throw this to indicate that the content doesn't exist. The generated express handler will return a 404 in this case.
|
- `ContentDoesNotExistError`: Throw this to indicate that the content doesn't exist. The generated express handler will return a 404 in this case.
|
||||||
> Note: Any message provided to the `ContentDoesNotExistError` object is returned to the client.
|
> Note: Any message provided to the `ContentDoesNotExistError` object is returned to the client.
|
||||||
|
|
||||||
## Content object:
|
## Content object:
|
||||||
|
|
||||||
This object contains metadata and methods which describe the content. The `ContentProvider` method builds and returns it.
|
This object contains metadata and methods which describe the content. The `ContentProvider` method builds and returns it.
|
||||||
|
|
||||||
### Properties:
|
### Properties:
|
||||||
|
|
||||||
All the properties of this object are used to return content metadata to the client as various `Response` headers.
|
All the properties of this object are used to return content metadata to the client as various `Response` headers.
|
||||||
|
|
||||||
- `fileName`: Used as the `Content-Disposition` header's `filename` value.
|
- `fileName`: Used as the `Content-Disposition` header's `filename` value.
|
||||||
- `mimeType`: Used as the `Content-Type` header value.
|
- `mimeType`: Used as the `Content-Type` header value.
|
||||||
- `totalSize`: Used as the `Content-Length` header value.
|
- `totalSize`: Used as the `Content-Length` header value.
|
||||||
|
|
||||||
### Methods:
|
### Methods:
|
||||||
|
|
||||||
- `getStream(range?: Range)`: This method should return a readable stream initialized to the provided `range` (optional). You need to handle two cases:
|
- `getStream(range?: Range)`: This method should return a readable stream initialized to the provided `range` (optional). You need to handle two cases:
|
||||||
|
|
||||||
- range is `null`: When `range` is not-specified, the client is requesting the full content. In this case, return the stream as it is.
|
- range is `null`: When `range` is not-specified, the client is requesting the full content. In this case, return the stream as it is.
|
||||||
- range is `{start, end}`: When client requests partial content, the `start` and `end` values will point to the corresponding byte positions (0 based and inclusive) of the content. You need to return stream limited to these positions.
|
- range is `{start, end}`: When client requests partial content, the `start` and `end` values will point to the corresponding byte positions (0 based and inclusive) of the content. You need to return stream limited to these positions.
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import { Range } from "./Range";
|
import { Range } from "./Range";
|
||||||
import { Stream } from "stream";
|
import { Stream } from "stream";
|
||||||
export interface Content {
|
export interface Content {
|
||||||
/**
|
/**
|
||||||
* Returns a readable stream based on the provided range (optional).
|
* Returns a readable stream based on the provided range (optional).
|
||||||
* @param {Range} range The start-end range of stream data.
|
* @param {Range} range The start-end range of stream data.
|
||||||
* @returns {Stream} A readable stream
|
* @returns {Stream} A readable stream
|
||||||
*/
|
*/
|
||||||
getStream(range?: Range): Stream;
|
getStream(range?: Range): Stream;
|
||||||
/**
|
/**
|
||||||
* Total size of the content
|
* Total size of the content
|
||||||
*/
|
*/
|
||||||
readonly totalSize: number;
|
readonly totalSize: number;
|
||||||
/**
|
/**
|
||||||
* Mime type to be sent in Content-Type header
|
* Mime type to be sent in Content-Type header
|
||||||
*/
|
*/
|
||||||
readonly mimeType: string;
|
readonly mimeType: string;
|
||||||
/**
|
/**
|
||||||
* File name to be sent in Content-Disposition header
|
* File name to be sent in Content-Disposition header
|
||||||
*/
|
*/
|
||||||
readonly fileName: string;
|
readonly fileName: string;
|
||||||
};
|
};
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
export class ContentDoesNotExistError extends Error {
|
export class ContentDoesNotExistError extends Error {
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
import { Content } from "./Content";
|
import { Content } from "./Content";
|
||||||
/**
|
/**
|
||||||
* @type {function (Request): Promise<Content>}
|
* @type {function (Request): Promise<Content>}
|
||||||
*/
|
*/
|
||||||
export type ContentProvider = (req: Request) => Promise<Content>;
|
export type ContentProvider = (req: Request) => Promise<Content>;
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export interface Logger {
|
export interface Logger {
|
||||||
debug(message: string, extra?: any): void;
|
debug(message: string, extra?: any): void;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export class RangeParserError extends Error {
|
export class RangeParserError extends Error {
|
||||||
constructor(start: any, end: any) {
|
constructor(start: any, end: any) {
|
||||||
super(`Invalid start and end values: ${start}-${end}.`);
|
super(`Invalid start and end values: ${start}-${end}.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,60 +1,60 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { parseRangeHeader } from "./parseRangeHeader";
|
import { parseRangeHeader } from "./parseRangeHeader";
|
||||||
import { RangeParserError } from "./RangeParserError";
|
import { RangeParserError } from "./RangeParserError";
|
||||||
import { Logger } from "./Logger";
|
import { Logger } from "./Logger";
|
||||||
import { ContentProvider } from "./ContentProvider";
|
import { ContentProvider } from "./ContentProvider";
|
||||||
import { ContentDoesNotExistError } from "./ContentDoesNotExistError";
|
import { ContentDoesNotExistError } from "./ContentDoesNotExistError";
|
||||||
import {
|
import {
|
||||||
getRangeHeader,
|
getRangeHeader,
|
||||||
setContentRangeHeader,
|
setContentRangeHeader,
|
||||||
setContentTypeHeader,
|
setContentTypeHeader,
|
||||||
setContentDispositionHeader,
|
setContentDispositionHeader,
|
||||||
setAcceptRangesHeader,
|
setAcceptRangesHeader,
|
||||||
setContentLengthHeader,
|
setContentLengthHeader,
|
||||||
setCacheControlHeaderNoCache
|
setCacheControlHeaderNoCache
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
export function createPartialContentHandler(contentProvider: ContentProvider, logger: Logger) {
|
export function createPartialContentHandler(contentProvider: ContentProvider, logger: Logger) {
|
||||||
return async function handler(req: Request, res: Response) {
|
return async function handler(req: Request, res: Response) {
|
||||||
let content;
|
let content;
|
||||||
try {
|
try {
|
||||||
content = await contentProvider(req);
|
content = await contentProvider(req);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.debug("createPartialContentHandler: ContentProvider threw exception: ", error);
|
logger.debug("createPartialContentHandler: ContentProvider threw exception: ", error);
|
||||||
if (error instanceof ContentDoesNotExistError) {
|
if (error instanceof ContentDoesNotExistError) {
|
||||||
return res.status(404).send(error.message);
|
return res.status(404).send(error.message);
|
||||||
}
|
}
|
||||||
return res.sendStatus(500);
|
return res.sendStatus(500);
|
||||||
}
|
}
|
||||||
let { getStream, mimeType, fileName, totalSize } = content;
|
let { getStream, mimeType, fileName, totalSize } = content;
|
||||||
const rangeHeader = getRangeHeader(req);
|
const rangeHeader = getRangeHeader(req);
|
||||||
let range;
|
let range;
|
||||||
try {
|
try {
|
||||||
range = parseRangeHeader(rangeHeader, totalSize, logger);
|
range = parseRangeHeader(rangeHeader, totalSize, logger);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.debug(`createPartialContentHandler: parseRangeHeader error: `, error);
|
logger.debug(`createPartialContentHandler: parseRangeHeader error: `, error);
|
||||||
if (error instanceof RangeParserError) {
|
if (error instanceof RangeParserError) {
|
||||||
setContentRangeHeader(null, totalSize, res);
|
setContentRangeHeader(null, totalSize, res);
|
||||||
return res.status(416).send(`Invalid value for Range: ${rangeHeader}`);
|
return res.status(416).send(`Invalid value for Range: ${rangeHeader}`);
|
||||||
}
|
}
|
||||||
return res.sendStatus(500);
|
return res.sendStatus(500);
|
||||||
}
|
}
|
||||||
setContentTypeHeader(mimeType, res);
|
setContentTypeHeader(mimeType, res);
|
||||||
setContentDispositionHeader(fileName, res);
|
setContentDispositionHeader(fileName, res);
|
||||||
setAcceptRangesHeader(res);
|
setAcceptRangesHeader(res);
|
||||||
// If range is not specified, or the file is empty, return the full stream
|
// If range is not specified, or the file is empty, return the full stream
|
||||||
if (range === null) {
|
if (range === null) {
|
||||||
logger.debug("createPartialContentHandler: No range found, returning full content.");
|
logger.debug("createPartialContentHandler: No range found, returning full content.");
|
||||||
setContentLengthHeader(totalSize, res);
|
setContentLengthHeader(totalSize, res);
|
||||||
return getStream().pipe(res);
|
return getStream().pipe(res);
|
||||||
}
|
}
|
||||||
setContentRangeHeader(range, totalSize, res);
|
setContentRangeHeader(range, totalSize, res);
|
||||||
let { start, end } = range;
|
let { start, end } = range;
|
||||||
setContentLengthHeader(start === end ? 0 : end - start + 1, res);
|
setContentLengthHeader(start === end ? 0 : end - start + 1, res);
|
||||||
setCacheControlHeaderNoCache(res);
|
setCacheControlHeaderNoCache(res);
|
||||||
// Return 206 Partial Content status
|
// Return 206 Partial Content status
|
||||||
logger.debug("createPartialContentHandler: Returning partial content for range: ", JSON.stringify(range));
|
logger.debug("createPartialContentHandler: Returning partial content for range: ", JSON.stringify(range));
|
||||||
res.status(206);
|
res.status(206);
|
||||||
|
|
||||||
return getStream(range).pipe(res);
|
return getStream(range).pipe(res);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
|
||||||
import { Range, ContentDoesNotExistError, ContentProvider } from "../../index";
|
import { Range, ContentDoesNotExistError, ContentProvider } from "../../index";
|
||||||
import { existsAsync, statAsync } from "./utils";
|
import { existsAsync, statAsync } from "./utils";
|
||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
|
|
||||||
export const fileContentProvider: ContentProvider = async (req: Request) => {
|
export const fileContentProvider: ContentProvider = async (req: Request) => {
|
||||||
const fileName = req.params.name;
|
const fileName = req.params.name;
|
||||||
const file = `${__dirname}/../sample-files/${fileName}`;
|
const file = `${__dirname}/../sample-files/${fileName}`;
|
||||||
if (!(await existsAsync(file))) {
|
if (!(await existsAsync(file))) {
|
||||||
throw new ContentDoesNotExistError(`File doesn't exist: ${file}`);
|
throw new ContentDoesNotExistError(`File doesn't exist: ${file}`);
|
||||||
}
|
}
|
||||||
const stats = await statAsync(file);
|
const stats = await statAsync(file);
|
||||||
const totalSize = stats.size;
|
const totalSize = stats.size;
|
||||||
const mimeType = "application/octet-stream";
|
const mimeType = "application/octet-stream";
|
||||||
const getStream = (range?: Range) => {
|
const getStream = (range?: Range) => {
|
||||||
if (!range) {
|
if (!range) {
|
||||||
return fs.createReadStream(file);
|
return fs.createReadStream(file);
|
||||||
}
|
}
|
||||||
const { start, end } = range;
|
const { start, end } = range;
|
||||||
logger.debug(`start: ${start}, end: ${end}`);
|
logger.debug(`start: ${start}, end: ${end}`);
|
||||||
return fs.createReadStream(file, { start, end });
|
return fs.createReadStream(file, { start, end });
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
fileName,
|
fileName,
|
||||||
totalSize,
|
totalSize,
|
||||||
mimeType,
|
mimeType,
|
||||||
getStream
|
getStream
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
import { createPartialContentHandler } from "../../index";
|
import { createPartialContentHandler } from "../../index";
|
||||||
import { fileContentProvider } from "./fileContentProvider";
|
import { fileContentProvider } from "./fileContentProvider";
|
||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
|
|
||||||
const handler = createPartialContentHandler(fileContentProvider, logger);
|
const handler = createPartialContentHandler(fileContentProvider, logger);
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = 8080;
|
const port = 8080;
|
||||||
|
|
||||||
app.get("/files/:name", handler);
|
app.get("/files/:name", handler);
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
logger.debug("Server started!");
|
logger.debug("Server started!");
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
export const logger = {
|
export const logger = {
|
||||||
debug(message: string, extra?: any) {
|
debug(message: string, extra?: any) {
|
||||||
if (extra) {
|
if (extra) {
|
||||||
console.log(`[debug]: ${message}`, extra);
|
console.log(`[debug]: ${message}`, extra);
|
||||||
} else {
|
} else {
|
||||||
console.log(`[debug]: ${message}`);
|
console.log(`[debug]: ${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
|
||||||
export const statAsync = promisify(fs.stat);
|
export const statAsync = promisify(fs.stat);
|
||||||
export const existsAsync = promisify(fs.exists);
|
export const existsAsync = promisify(fs.exists);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||||
Magna etiam tempor orci eu lobortis elementum nibh. In egestas erat imperdiet sed euismod. Amet consectetur adipiscing elit pellentesque habitant.
|
Magna etiam tempor orci eu lobortis elementum nibh. In egestas erat imperdiet sed euismod. Amet consectetur adipiscing elit pellentesque habitant.
|
||||||
Vel quam elementum pulvinar etiam non quam lacus suspendisse.
|
Vel quam elementum pulvinar etiam non quam lacus suspendisse.
|
||||||
Nibh sit amet commodo nulla facilisi. Vel risus commodo viverra maecenas accumsan lacus. Ornare arcu dui vivamus arcu felis bibendum ut tristique et.
|
Nibh sit amet commodo nulla facilisi. Vel risus commodo viverra maecenas accumsan lacus. Ornare arcu dui vivamus arcu felis bibendum ut tristique et.
|
||||||
Vitae semper quis lectus nulla at volutpat diam. Mauris vitae ultricies leo integer malesuada nunc. Donec massa sapien faucibus et. Senectus et netus et malesuada. Vitae tortor condimentum lacinia quis vel. Sagittis id consectetur purus ut faucibus pulvinar elementum. Nisi est sit amet facilisis magna etiam tempor orci eu.
|
Vitae semper quis lectus nulla at volutpat diam. Mauris vitae ultricies leo integer malesuada nunc. Donec massa sapien faucibus et. Senectus et netus et malesuada. Vitae tortor condimentum lacinia quis vel. Sagittis id consectetur purus ut faucibus pulvinar elementum. Nisi est sit amet facilisis magna etiam tempor orci eu.
|
||||||
|
|
||||||
Dictum varius duis at consectetur lorem donec massa sapien. Odio pellentesque diam volutpat commodo. Egestas dui id ornare arcu odio ut sem nulla. Consequat id porta nibh venenatis cras sed felis eget. Placerat in egestas erat imperdiet. Dui nunc mattis enim ut tellus elementum sagittis vitae. Aliquet bibendum enim facilisis gravida neque convallis a cras. Id semper risus in hendrerit gravida. Tempor orci eu lobortis elementum nibh tellus molestie. Semper auctor neque vitae tempus quam pellentesque. Vitae proin sagittis nisl rhoncus mattis rhoncus urna neque. Bibendum at varius vel pharetra vel turpis. Tellus integer feugiat scelerisque varius morbi enim nunc. Volutpat commodo sed egestas egestas fringilla. Congue eu consequat ac felis donec et odio. Venenatis cras sed felis eget velit aliquet. Urna neque viverra justo nec. Dictum non consectetur a erat nam. Lacinia quis vel eros donec.
|
Dictum varius duis at consectetur lorem donec massa sapien. Odio pellentesque diam volutpat commodo. Egestas dui id ornare arcu odio ut sem nulla. Consequat id porta nibh venenatis cras sed felis eget. Placerat in egestas erat imperdiet. Dui nunc mattis enim ut tellus elementum sagittis vitae. Aliquet bibendum enim facilisis gravida neque convallis a cras. Id semper risus in hendrerit gravida. Tempor orci eu lobortis elementum nibh tellus molestie. Semper auctor neque vitae tempus quam pellentesque. Vitae proin sagittis nisl rhoncus mattis rhoncus urna neque. Bibendum at varius vel pharetra vel turpis. Tellus integer feugiat scelerisque varius morbi enim nunc. Volutpat commodo sed egestas egestas fringilla. Congue eu consequat ac felis donec et odio. Venenatis cras sed felis eget velit aliquet. Urna neque viverra justo nec. Dictum non consectetur a erat nam. Lacinia quis vel eros donec.
|
26
src/utils.ts
26
src/utils.ts
@ -1,13 +1,13 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { Range } from "./Range";
|
import { Range } from "./Range";
|
||||||
export const getHeader = (name: string, req: Request) => req.headers[name];
|
export const getHeader = (name: string, req: Request) => req.headers[name];
|
||||||
export const getRangeHeader = getHeader.bind(null, "range");
|
export const getRangeHeader = getHeader.bind(null, "range");
|
||||||
export const setHeader = (name: string, value: string, res: Response) => res.setHeader(name, value);
|
export const setHeader = (name: string, value: string, res: Response) => res.setHeader(name, value);
|
||||||
export const setContentTypeHeader = setHeader.bind(null, "Content-Type");
|
export const setContentTypeHeader = setHeader.bind(null, "Content-Type");
|
||||||
export const setContentLengthHeader = setHeader.bind(null, "Content-Length");
|
export const setContentLengthHeader = setHeader.bind(null, "Content-Length");
|
||||||
export const setAcceptRangesHeader = setHeader.bind(null, "Accept-Ranges", "bytes");
|
export const setAcceptRangesHeader = setHeader.bind(null, "Accept-Ranges", "bytes");
|
||||||
export const setContentRangeHeader = (range: Range | null, size: number, res: Response) =>
|
export const setContentRangeHeader = (range: Range | null, size: number, res: Response) =>
|
||||||
setHeader("Content-Range", `bytes ${range ? `${range.start}-${range.end}` : "*"}/${size}`, res);
|
setHeader("Content-Range", `bytes ${range ? `${range.start}-${range.end}` : "*"}/${size}`, res);
|
||||||
export const setContentDispositionHeader = (fileName: string, res: Response) =>
|
export const setContentDispositionHeader = (fileName: string, res: Response) =>
|
||||||
setHeader("Content-Disposition", `attachment; filename*=utf-8''${encodeURIComponent(fileName)}`, res);
|
setHeader("Content-Disposition", `attachment; filename*=utf-8''${encodeURIComponent(fileName)}`, res);
|
||||||
export const setCacheControlHeaderNoCache = setHeader.bind(null, "Cache-Control", "no-cache");
|
export const setCacheControlHeaderNoCache = setHeader.bind(null, "Cache-Control", "no-cache");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user