devicarus / image-to-pdf

A simple 🖼️ to 📄 converter for NodeJS
https://www.npmjs.com/package/image-to-pdf
24 stars 7 forks source link

ENAMETOOLONG: name too long, open '...' #7

Open barrownicholas opened 1 year ago

barrownicholas commented 1 year ago

Hello, when I attempt to pass a list of base-64 URLs into the plugin, it fails and outputs ENAMETOOLONG: name too long, open '...' where the ... appears to be the base 64 of the item in the first position of the list. I'm attaching the files I attempted to use. Test PDF A - 3 Pages-1 Test PDF A - 3 Pages-2 Test PDF A - 3 Pages-3

function(properties, context) {

    let pdf_url = "";
    let error_occurred = false;
    let error_message = "";

    const imgToPDF = require('image-to-pdf');
    const axios = require('axios').default;

    function isValidHttpUrl(string) {
        // https://stackoverflow.com/questions/5717093/check-if-a-javascript-string-is-a-url
        let url;
        try { url = new URL(string); }
        catch (_) { return false; }
        return url.protocol === "http:" || url.protocol === "https:";
    }

    // https://stackoverflow.com/a/44058739/14717625
    function getBase64(url) {
        return axios
            .get(url, {
            responseType: 'arraybuffer'
        })
            .then(response => Buffer.from(response.data, 'binary').toString('base64'))
    }

    /**
    * Convert a Readable Stream to base64 string
    * @param {ReadableStream} stream - a readable stream to convert in base64 string
    * @returns {Promise} - Promise that resolve in a string containing the base64
    *
    * https://gist.github.com/luan0ap/521325c619413a02a798482e2c06af90
    */
    const streamToBase64 = (stream) => {
        const concat = require('concat-stream')
        const { Base64Encode } = require('base64-stream')

        return new Promise((resolve, reject) => {
            const base64 = new Base64Encode()

            const cbConcat = (base64) => {
                resolve(base64)
            }

            stream
                .pipe(base64)
                .pipe(concat(cbConcat))
                .on('error', (error) => {
                reject(error)
            })
        })
    }

    function uploadBase64Server_Side(filename_, export_) {
        const uploaded_file_url = context.async(async callback => {
            try {

                const fileName = filename_.slice(-4) === ".pdf" ? filename_ : filename_ + ".pdf";

                let payload = {
                    name: fileName,
                    contents: export_,
                    private: false
                }

                var options = {
                    uri: properties.website_home_url + "fileupload",
                    method: "POST",
                    json: payload
                }

                var fileurl = context.request(options).body;

                callback(null, "https:" + fileurl);
            }
            catch (err) {
                error_occurred = true;
                error_message = "An error occurred while uploading the workbook file to the database: " + err;
            }
        });
        return uploaded_file_url;
    }

    const urls = properties.urls ? properties.urls.get(0, properties.urls.length()) : [];

    if(!urls) {
        error_occurred = true;
        error_message = "No images were provided (possibly empty)";
    }
    else {

        // clean up the URLs
        for(let i = 0; i < urls.length; i++) {

            // if URL is invalid
            if(!isValidHttpUrl(urls[i])) {

                // test if adding the prefix is valid
                if(isValidHttpUrl("https:" + urls[i])) { urls[i] = "https:" + urls[i]; }
                else {
                    error_occurred = true;
                    error_message = `The URL provided at index ${i} is invalid`;
                }
            }

        }

        // 
        if(!error_occurred) {

            const base64s = [];

            // presize base64s
            for(let _ = 0; _ < urls.length; _++) {
                base64s.push("");
            }

            // load the base64s
            for(let i = 0; i < urls.length; i++) {

                const b64 = context.async(async callback => {
                    const url = urls[i];
                    const val = await getBase64(url);
                    callback(null, val);
                });

                base64s[i] = b64;
            }

            // get the base64 for the PDF
            const pdf_base_64 = context.async(async callback => {

                const stream_ = await imgToPDF(base64s, imgToPDF.sizes.LETTER);

                // const base64 = streamToBase64(stream_);

                callback(null, null);

            });

            // upload the file
            pdf_url = uploadBase64Server_Side("file.pdf", pdf_base_64);

        }

    }

    return {
        "pdf_url": pdf_url,
        "error_occurred": error_occurred,
        "error_message": error_message
    }

}
Txmo commented 7 months ago

Hi, the error can be reproduced when passing the "raw" base64 string to the imagToPDF-Function. What you need instead is a data URL (https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs). If you want to convert PNGs for example, the data URL would be: data:image/png;bas64,yourbase64here. So you have to prepend your base64 with the fitting data:...-prefix.

I guess the underlying pdfkit-package does not realize that the passed data is base64 without the data:...-prefix and interprets it as a filename and hence the ENAMETOOLONG error.