Files: 781e1c19862d0d3327dd9a68e0c497d9f876cc0f / blob / html / input.js
7412 bytesRaw
1 | var h = require('mutant/h') |
2 | var resolve = require('mutant/resolve') |
3 | var onceTrue = require('mutant/once-true') |
4 | var pull = require('pull-stream') |
5 | var mime = require('simple-mime')('application/octect-stream') |
6 | var split = require('split-buffer') |
7 | var nest = require('depnest') |
8 | var Defer = require('pull-defer') |
9 | var BoxStream = require('pull-box-stream') |
10 | var crypto = require('crypto') |
11 | var zeros = Buffer.alloc(24, 0) |
12 | var piexif = require('piexifjs') |
13 | |
14 | module.exports = { |
15 | needs: nest({ |
16 | 'sbot.obs.connection': 'first' |
17 | }), |
18 | gives: nest('blob.html.input'), |
19 | create: function (api) { |
20 | return nest('blob.html.input', function FileInput (onAdded, opts = {}) { |
21 | return h('input', { |
22 | accept: opts.accept, |
23 | type: 'file', |
24 | 'ev-change': function (ev) { |
25 | var file = ev.target.files[0] |
26 | if (!file) return |
27 | |
28 | var mimeType = mime(file.name) |
29 | var fileName = file.name |
30 | |
31 | getFileData(file, function(fileData) { |
32 | var orientation = 0; |
33 | if (mimeType == "image/jpeg") { |
34 | try { |
35 | orientation = getOrientation(fileData) |
36 | |
37 | if ((typeof opts.removeExif == 'function' && opts.removeExif()) || |
38 | opts.removeExif === true) |
39 | fileData = removeExif(fileData, orientation) |
40 | } |
41 | catch (ex) |
42 | { |
43 | console.log("exif exception:", ex) |
44 | } |
45 | } |
46 | |
47 | // handle exif orientation data and resize |
48 | if (orientation >= 3 || opts.resize) { |
49 | getImage(fileData, (image) => { |
50 | image = rotate(image, orientation) |
51 | if (opts.resize) { |
52 | image = resize(image, opts.resize.width, opts.resize.height) |
53 | } |
54 | if (image.toBlob) { |
55 | if (mimeType !== 'image/jpeg' && mimeType !== 'image/png') { |
56 | mimeType = 'image/jpeg' |
57 | } |
58 | image.toBlob(blob => { |
59 | next(blob) |
60 | }, mimeType, 0.85) |
61 | } else { |
62 | next(dataURItoBlob(fileData)) |
63 | } |
64 | }) |
65 | } else { |
66 | // don't process |
67 | next(dataURItoBlob(fileData)) |
68 | } |
69 | }) |
70 | |
71 | function dataURItoBlob(dataURI) { |
72 | var byteString = atob(dataURI.split(',')[1]); |
73 | var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0] |
74 | var ab = new ArrayBuffer(byteString.length); |
75 | var ia = new Uint8Array(ab); |
76 | for (var i = 0; i < byteString.length; i++) { |
77 | ia[i] = byteString.charCodeAt(i); |
78 | } |
79 | return new Blob([ab], {type: mimeString}); |
80 | } |
81 | |
82 | function next (file) { |
83 | var reader = new global.FileReader() |
84 | reader.onload = function () { |
85 | var stream = pull.values(split(new Buffer(reader.result), 64 * 1024)) |
86 | pull(stream, AddBlob({ |
87 | connection: api.sbot.obs.connection, |
88 | encrypt: resolve(opts.private) |
89 | }, (err, blob) => { |
90 | if (err) return console.error(err) |
91 | onAdded({ |
92 | link: blob, |
93 | name: fileName, |
94 | size: reader.result.length || reader.result.byteLength, |
95 | type: mimeType |
96 | }) |
97 | |
98 | ev.target.value = '' |
99 | })) |
100 | } |
101 | reader.readAsArrayBuffer(file) |
102 | } |
103 | } |
104 | }) |
105 | }) |
106 | } |
107 | } |
108 | |
109 | function getImage (file, cb) { |
110 | var image = document.createElement('img') |
111 | image.onload = () => cb(image) |
112 | image.src = file |
113 | image.style.display = 'block' |
114 | if (image.complete) cb(image) |
115 | } |
116 | |
117 | function resize (image, width, height) { |
118 | var imageHeight = image.height |
119 | var imageWidth = image.width |
120 | |
121 | var multiplier = (height / image.height) |
122 | if (multiplier * imageWidth < width) { |
123 | multiplier = width / image.width |
124 | } |
125 | |
126 | var finalWidth = imageWidth * multiplier |
127 | var finalHeight = imageHeight * multiplier |
128 | |
129 | var offsetX = (finalWidth - width) / 2 |
130 | var offsetY = (finalHeight - height) / 2 |
131 | |
132 | var canvas = document.createElement('canvas') |
133 | canvas.width = width |
134 | canvas.height = height |
135 | var ctx = canvas.getContext('2d') |
136 | ctx.drawImage(image, -offsetX, -offsetY, finalWidth, finalHeight) |
137 | return canvas |
138 | } |
139 | |
140 | function getFileData(file, cb) |
141 | { |
142 | var reader = new global.FileReader() |
143 | reader.onload = function (e) { |
144 | cb(e.target.result) |
145 | } |
146 | reader.readAsDataURL(file) |
147 | } |
148 | |
149 | function removeExif (fileData, orientation) { |
150 | var clean = piexif.remove(fileData) |
151 | if (orientation != undefined) { // preserve |
152 | var exifData = { "0th": {} } |
153 | exifData["0th"][piexif.ImageIFD.Orientation] = orientation |
154 | var exifStr = piexif.dump(exifData) |
155 | return piexif.insert(exifStr, clean) |
156 | } |
157 | else |
158 | return clean |
159 | } |
160 | |
161 | function getOrientation (fileData) { |
162 | var exif = piexif.load(fileData); |
163 | return exif["0th"][piexif.ImageIFD.Orientation] |
164 | } |
165 | |
166 | function rotate (img, orientation) { |
167 | var canvas = document.createElement('canvas') |
168 | var ctx = canvas.getContext('2d') |
169 | |
170 | if (orientation === 6 || orientation === 8) { |
171 | canvas.width = img.height |
172 | canvas.height = img.width |
173 | ctx.translate(img.height / 2, img.width / 2) |
174 | if (orientation === 6) { |
175 | ctx.rotate(0.5 * Math.PI) |
176 | } else { |
177 | ctx.rotate(1.5 * Math.PI) |
178 | } |
179 | } else if (orientation === 3) { |
180 | canvas.width = img.width |
181 | canvas.height = img.height |
182 | ctx.translate(img.width / 2, img.height / 2) |
183 | ctx.rotate(1 * Math.PI) |
184 | } else { |
185 | return img |
186 | } |
187 | |
188 | ctx.drawImage(img, -img.width / 2, -img.height / 2) |
189 | return canvas |
190 | } |
191 | |
192 | function AddBlob ({connection, encrypt = false}, cb) { |
193 | var stream = Defer.sink() |
194 | onceTrue(connection, sbot => { |
195 | if (encrypt) { |
196 | // FROM: https://github.com/ssbc/ssb-secret-blob/blob/master/index.js |
197 | // here we need to hash something twice, first, hash the plain text to use as the |
198 | // key. This has the benefit of encrypting deterministically - the same file will |
199 | // have the same hash. This can be used to deduplicate storage, but has privacy |
200 | // implications. I do it here just because it's early days and this makes testing |
201 | // easier. |
202 | |
203 | stream.resolve(Hash(function (err, buffers, key) { |
204 | if (err) return cb(err) |
205 | pull( |
206 | pull.once(Buffer.concat(buffers)), |
207 | BoxStream.createBoxStream(key, zeros), |
208 | Hash(function (err, buffers, hash) { |
209 | if (err) return cb(err) |
210 | var id = '&' + hash.toString('base64') + '.sha256' |
211 | pull( |
212 | pull.values(buffers), |
213 | sbot.blobs.add(id, function (err) { |
214 | if (err) return cb(err) |
215 | sbot.blobs.push(id, function (err) { |
216 | if (err) return cb(err) |
217 | cb(null, id + '?unbox=' + key.toString('base64') + '.boxs') |
218 | }) |
219 | }) |
220 | ) |
221 | }) |
222 | ) |
223 | })) |
224 | } else { |
225 | stream.resolve(sbot.blobs.add(cb)) |
226 | } |
227 | }) |
228 | return stream |
229 | } |
230 | |
231 | function Hash (cb) { |
232 | var hash = crypto.createHash('sha256') |
233 | var buffers = [] |
234 | var hasher = pull.drain(function (data) { |
235 | data = typeof data === 'string' ? new Buffer(data) : data |
236 | buffers.push(data) |
237 | hash.update(data) |
238 | }, function (err) { |
239 | cb(err, buffers, hash.digest()) |
240 | }) |
241 | return hasher |
242 | } |
243 |
Built with git-ssb-web