UNPKG

node-web-mvc

Version:
155 lines (154 loc) 5.82 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const crypto_1 = require("crypto"); const MediaType_1 = __importDefault(require("../MediaType")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const MultipartFile_1 = __importDefault(require("../MultipartFile")); const EntityTooLargeError_1 = __importDefault(require("../../../errors/EntityTooLargeError")); class MultipartSubpart { get currentBuffer() { return Buffer.from(this.raw); } constructor(boundary, config, raw) { this.boundary = boundary; this.headers = {}; this.raw = raw || []; this.tempRaw = []; this.size = 0; this.status = 'boundary'; this.mediaRoot = config.mediaRoot; this.maxFileSize = Number(config.maxFileSize); this.chunkSize = Math.max(8192, boundary.length + 10); } read(code) { const raw = this.raw; const isCRLF = code == 10 && this.previousCode == 13; this.previousCode = code; switch (this.status) { case 'body': if (isCRLF && !this.needTryBoundary) { // 在读取subaprt的body时,如果碰到\r\n 则需要进入边界检测 this.needTryBoundary = true; // 移除最后已经读取的\r this.tempRaw.pop(); // 将已经读取内容写出 this.tryWrite(); // 返回true 继续读取下个字节 return true; } this.tempRaw.push(code); if (this.tempRaw.length > this.chunkSize) { this.tryWrite(); } // 根据读取进度检查边界 return this.checkBoundary(); default: raw.push(code); // 如果是读取header部分,碰到\r\n则进入下一个内容读取 if (isCRLF) { // 这里需要把最后已经读取的\r\n移除 raw.pop(); raw.pop(); } return !isCRLF; } } finish(encoding) { if (this.writter) { const tempPath = this.writter.path.toString(); const promise = new Promise((resolve) => { this.writter.end(resolve); }); return { content: new MultipartFile_1.default(this.filename, tempPath, this.mediaType, this.size, this.mediaRoot), promise: promise, }; } else { return { content: this.currentBuffer.toString(encoding), promise: null, }; } } clearBuffer() { this.raw.length = 0; } checkBoundary() { const raw = this.tempRaw; if (this.needTryBoundary && raw.length == this.boundary.length) { this.needTryBoundary = false; // 检测是否为结束或者开始boundary const str = Buffer.from(raw).toString(); if (str == this.boundary) { // 如果时边界,则返回停止读取,进入结束流程 return false; } // 如果不是边界,则需要把\r\n补充 raw.unshift(10); raw.unshift(13); } // 返回继续读取 return true; } tryWrite() { const tempRaw = this.tempRaw; if (this.writter) { const buffer = Buffer.from(tempRaw); this.size = this.size + buffer.length; if (this.size > this.maxFileSize) { this.writter.end(); fs_1.default.unlinkSync(this.writter.path); throw new EntityTooLargeError_1.default(this.filename, this.size, this.maxFileSize); } this.writter.write(buffer); } else { this.raw.push(...tempRaw); } this.tempRaw.length = 0; } parseContentDisposition(content) { const segments = content.split(';'); const info = {}; segments.forEach((segment) => { const [k, v] = segment.trim().split('='); info[k] = v ? v.slice(1, v.length - 1) : ''; }); return info; } parseSubpartHeader(content) { var _a; if (!content) { const tempRoot = path_1.default.join(this.mediaRoot, 'tmp-files'); MultipartFile_1.default.ensureDirSync(tempRoot); this.writter = this.isFile ? fs_1.default.createWriteStream(path_1.default.join(tempRoot, (0, crypto_1.randomUUID)())) : undefined; return false; } const [keyName, value] = content.split(':'); const key = keyName.trim(); switch (key.toLowerCase()) { case 'content-disposition': { const s = this.parseContentDisposition(value || ''); this.name = s.name; this.isFile = 'filename' in s; this.filename = s.filename; this.headers[key] = value; } break; case 'content-type': this.mediaType = new MediaType_1.default((_a = value === null || value === void 0 ? void 0 : value.trim) === null || _a === void 0 ? void 0 : _a.call(value)); this.headers[key] = value; break; default: this.headers[key] = value; } return true; } } exports.default = MultipartSubpart;