typescriptmemorystreambytebuffer

How to create Javascript WritableStream on memory (like fs.creareWriteStream)


I try to use some JS library with ServiceWorker, without access to Node.JS FileSystem functionality. That original library export result in the same way

Import * as fs from 'fs'
...
JsLib.export(result, fs.createWriteStream("res.pdf")) 

I need the same export, but export to byte array, without FileSystem. I have create array in the same way

 const Out = (Array as any).create("byte", mysize)

and if I manually fill this byte array with test data, my algorithm working fine further.
But I don't understand how to correctly create the same stream as Node.JS created, but in memory, in my "Out" array.
There are some example in MDN with WritableStream, but I'm not sure how that examples related to this task. Maybe there is cheap solution like

JsLib.export(result, ...Out) 

Or this solution not emulated fs.createWriteStream? Directly using spread syntax is impossible "A spread argument must either have a tuple type or be passed to a rest parameter". But how to solve this quest correctly?

Upd. This is answer to @l3l_aze with very basic test, unfortunately, this solution not working.

Arraybuffer Directly


Solution

  • As I said above I don't know TS, so this is just NodeJS/vanilla JS. Highly suggest looking into this more than the couple hours I took as it may not be closing the stream properly, and obviously there's no error handling included.

    The output is a red square in PNG format though. To visually confirm the test, I copied the output and used the VSCode Hex Editor extension to paste it into an existing file.png with the base64 option selected. Then had to delete (Del, not Backspace) leftover bytes from the previous data I had to fill it with so that the hex editor would allow pasting. Lol.

    'use strict'
    
    import * as PImage from 'pureimage'
    import Stream from 'stream'
    
    const width = 32
    const height = 32
    const img1 = PImage.make(width, height)
    
    const ctx = img1.getContext('2d')
    
    ctx.fillStyle = 'red'
    ctx.fillRect(0, 0, width, height)
    
    const pngData = []
    
    const dest = new Stream()
    dest.writable = true
    
    dest.write = function (data) {
      for (let i = 0; i < data.length; i++) {
        dest.emit('data', data[i])
      }
    }
    
    // Act like a passThrough stream; in one ear and out the other.
    dest.on('data', (chunk) => {
      pngData.push(chunk)
    })
    
    dest.on('end', () => {})
    
    // For
    // https://github.com/joshmarinacci/node-pureimage/blob/6775bc3dedfd4247c0ee11382e1aebdf2703ca45/src/image.ts#L57
    dest.end = function () {
      dest.emit('finish')
    }
    
    dest.close = function () {
      dest.emit('close')
    }
    
    function assert (a, b) {
      if (a !== b) {
        throw new Error(`${a} !== ${b}`)
      } else {
        return true
      }
    }
    
    const buf = await PImage.encodePNGToStream(img1, dest).then(() => {
      return new Uint8Array(pngData)
    })
    
    if (assert(buf[0], 0x89) &&
        assert(buf[1], 0x50) &&
        assert(buf[2], 0x4e) &&
        assert(buf[3], 0x47)) {
      console.log('buffer contains png header')
    
      const ascii = Array.from(buf).map((b) => String.fromCharCode(b))
      const b64 = btoa(ascii.join(''))
    
      console.log(b64)
      // iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAklEQVR4AewaftIAAABBSURBVMXBAQEAAAiDMKR/5xuD7QYjJDGJSUxiEpOYxCQmMYlJTGISk5jEJCYxiUlMYhKTmMQkJjGJSUxiEpOYxB4w4wI+EhsQ6
    AAAAABJRU5ErkJggg==
    }