Added RNFS and refactor image upload to use it
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user