Added RNFS and refactor image upload to use it

This commit is contained in:
John Lyon-Smith
2018-05-14 10:38:38 -07:00
parent 6fae5ef5d6
commit eda43b0869
16 changed files with 174 additions and 109 deletions

View File

@@ -48,7 +48,6 @@
"redis": "^2.7.1",
"redis-rstream": "^0.1.3",
"regexp-pattern": "^1.0.4",
"safe-buffer": "^5.1.1",
"socket.io": "^2.0.3",
"tmp-promise": "^1.0.4",
"urlsafe-base64": "^1.0.0",

View File

@@ -6,12 +6,10 @@ import path from "path"
import util from "util"
import config from "config"
import autobind from "autobind-decorator"
import Buffer from "safe-buffer"
import B64 from "b64"
import { PassThrough } from "stream"
import { catchAll } from "."
function pipeToGridFS(readable, writable, decoder) {
function pipeToGridFS(readable, writeable) {
const promise = new Promise((resolve, reject) => {
readable.on("error", (error) => {
reject(error)
@@ -23,13 +21,13 @@ function pipeToGridFS(readable, writable, decoder) {
resolve(file)
})
})
readable.pipe(decoder).pipe(writeable)
readable.pipe(writeable)
return promise
}
@autobind
export class AssetRoutes {
static rangeRegex = /^(byte|base64) (\d+)/
static rangeRegex = /^byte (\d+)/
constructor(container) {
const app = container.app
@@ -73,13 +71,13 @@ export class AssetRoutes {
assetId = assetId.slice(0, extIndex)
}
const cursor = await this.db.gridfs.findOne({ _id: assetId })
const cursor = await this.db.gridfs.find({ _id: assetId })
const file = await cursor.next()
if (!cursor) {
if (!file) {
throw createError.NotFound(`Asset ${assetId} was not found`)
}
const file = cursor.next()
const ifNoneMatch = req.get("If-None-Match")
if (ifNoneMatch && ifNoneMatch === file.md5) {
@@ -138,7 +136,7 @@ export class AssetRoutes {
chunkContentType !== "application/base64"
) {
throw createError.BadRequest(
"chunkContentType must be application/octet-stream or application/base64"
"chunkContentType must be 'application/octet-stream' or 'application/base64'"
)
}
} else {
@@ -175,58 +173,47 @@ export class AssetRoutes {
throw createError.BadRequest(`Bad upload id ${uploadId}`)
}
if (!contentType.startsWith(uploadData.chunkContentType)) {
throw createError.BadRequest(
`Content-Type ${contentType} does not match chunk type ${
uploadData.chunkContentType
}`
)
}
if (parseInt(contentLength, 10) !== req.body.length) {
throw createError.BadRequest(
"Must supply Content-Length header matching length of request body"
)
}
let match = contentRange.match(AssetRoutes.rangeRegex)
if (!match || match.length !== 3) {
throw createError.BadRequest(
"Content-Range header must be supplied and of form '[byte|base64] <offset>'"
)
}
const [, contentOffsetUnit, contentOffset] = match
if (
(uploadData.chunkContentType === "application/octet-stream" &&
contentOffsetUnit !== "byte") ||
(uploadData.chunkContentType === "application/base64" &&
contentOffsetUnit !== "base64")
) {
throw createError.BadRequest(
`Content-Range offset unit must be ${
uploadData.chunkContentType === "application/base64"
? "base64"
: "byte"
}`
)
}
let offset = Number.parseInt(contentOffset)
if (offset < 0 || offset + req.body.length > uploadData.uploadSize) {
throw createError.BadRequest(
`Illegal Content-Range ${contentOffsetType} ${contentOffset} and Content-Length ${contentLength} for upload size ${
uploadData.uploadSize
}`
)
}
try {
if (!contentType.startsWith(uploadData.chunkContentType)) {
throw createError.BadRequest(
`Content-Type ${contentType} does not match chunk type ${
uploadData.chunkContentType
}`
)
}
if (parseInt(contentLength, 10) !== req.body.length) {
throw createError.BadRequest(
"Must supply Content-Length header matching length of request body"
)
}
let match = contentRange.match(AssetRoutes.rangeRegex)
if (!match || match.length !== 2) {
throw createError.BadRequest(
"Content-Range header must be supplied and of form 'byte <offset>'"
)
}
const [, contentOffset] = match
let offset = Number.parseInt(contentOffset)
const data =
uploadData.chunkContentType === "application/base64"
? Buffer.from(req.body, "base64")
: req.body
if (offset < 0 || offset + data.length > uploadData.uploadSize) {
throw createError.BadRequest(
`Illegal Content-Range 'byte ${contentOffset}' and Content-Length ${contentLength} for upload size ${
uploadData.uploadSize
}`
)
}
const [, uploadedChunks] = await Promise.all([
this.rs.setrangeAsync(uploadDataId, offset, req.body),
this.rs.setrangeAsync(uploadDataId, offset, data),
this.rs.incrAsync(uploadCountId),
])
const chunkInfo = {
@@ -242,11 +229,7 @@ export class AssetRoutes {
{ contentType: uploadData.contentType }
)
const decoder =
uploadData.chunkContentType === "application/base64"
? new B64.Decoder()
: new PassThrough()
const file = await pipeToGridFS(readable, writeable, decoder)
const file = await pipeToGridFS(readable, writeable)
await Promise.all([
this.rs.del(uploadId),
@@ -275,7 +258,6 @@ export class AssetRoutes {
this.rs.del(uploadId)
this.rs.del(uploadCountId)
this.rs.del(uploadDataId)
this.log.error(error.message)
throw error
}
}