-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathexpress-demo.js
128 lines (113 loc) · 3.95 KB
/
express-demo.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
'use strict' /* eslint-disable no-console */
const { createHash } = require('crypto')
const { createReadStream, createWriteStream, unlink } = require('fs')
const { basename, join } = require('path')
const { PassThrough } = require('stream')
const Canvas = require('canvas')
const express = require('express')
const multer = require('multer')
const libheif = require('./libheif')
const PORT = process.env.PORT
|| console.log('> Using default port')
|| 8e3
class ImageStorage {
constructor(options) {
this.getDestination = options && options.destination || ((_, __, cb) => cb(null, '/dev/null'))
}
_handleFile(req, file, cb) {
this.getDestination(req, file, (err, path) => {
if (err) return cb(err)
const skipDecode = file.mimetype.startsWith('image/')
if ( skipDecode ) return this._saveFile({ file, path, skipDecode }, cb)
const buf = []
let length = 0
file.stream
.on('error', cb)
.on('end', _ => {
const decoder = new libheif.HeifDecoder()
const [ image ] = decoder.decode( new Uint8Array(Buffer.concat(buf, length)) )
const w = image.get_width()
const h = image.get_height()
const canvas = new Canvas(w, h)
const ctx = canvas.getContext('2d')
const imageData = ctx.createImageData(w, h)
image.display(imageData, displayData => {
ctx.putImageData(displayData, 0, 0)
this._saveFile({ file, jpegStream: canvas.jpegStream(), path, skipDecode }, cb)
})
})
.on('data', chunk => {
length += chunk.length
buf.push(chunk)
})
})
}
_removeFile(_, file, cb) {
unlink(file.path, cb)
}
_saveFile({ file, jpegStream, path, skipDecode }, cb) {
const [ ext='' ] = file.originalname.match(/\.[^.]+?$/) || []
let fileName = file.originalname.slice(0, ext ? -1 * ext.length : undefined)
let checkSum
const hash = createHash('sha1')
.once('readable', _ => checkSum = hash.read().slice(0, 5).toString('hex'))
const passHash = new PassThrough()
const passWriter = new PassThrough()
;( jpegStream || file.stream )
.on('end', _ => {
passHash.pipe(hash, { end: false })
passHash
.on('error', err => {
hash.end()
cb(err)
})
.on('end', _ => hash.end(_ => {
fileName = join(path, fileName + '-' + checkSum + (skipDecode ? ext : '.jpeg'))
const fileWriter = createWriteStream( fileName )
.on('close', _ => cb(null, {
hash: checkSum,
path: fileName,
size: fileWriter.bytesWritten,
}))
passWriter.pipe(fileWriter)
passWriter
.on('error', err => {
fileWriter.end()
cb(err)
})
.end()
}))
.end()
})
.on('data', chunk => {
passHash.write(chunk)
passWriter.write(chunk)
})
}
}
const upload = multer({
fileFilter: (_, file, cb) => cb(
null,
file.mimetype.startsWith('image/')
|| file.mimetype === 'application/octet-stream'
&& /.+?\.hei[cf]$/.test(file.originalname)
),
fileSize: 2 * 1 << 20, // 2MB
storage: new ImageStorage({
destination: (_, __, cb) => cb(null, __dirname)
}),
})
express()
.get('/img/:fileName', (req, res, next) => {
createReadStream(join(__dirname, req.params.fileName)).on('error', next).pipe(res)
})
.post('/img/upload', upload.single('file'), (req, res, next) => {
if( !req.file ) return next( new Error('Unsupported File Format.') )
res.json({ ok: true, file_name: basename(req.file.path) })
})
.use((_, __, next) => next(new Error('Page Not Found')))
.use((err, req, res, _) => {
console.error('> %s\n', req.originalUrl, err && err.stack || err)
res.json({ ok: false, msg: err.message })
})
.listen(PORT, _ => console.log('> Server listening on port %d', PORT))