“This is the seventh day of my participation in the First Challenge 2022.

preface

Vue – based Web and desktop file download and preview summary. The project uses vUE 2.6.10, VUE – CLI 3.12.1, Node v14.17.5, and electron 12.0.0.

※ Note: “+” at the beginning of each line in the code area of this article means new, “-” means deleted, and “M” means modified; “… “in code Represents omission.

1 Web side

1.1 Download to a LOCAL PC

Ideas:

Typically, the back end will return a binary PDF file. In this case, for a native Ajax request, specify xhr.responseType = ‘blob’ to receive the BLOB object. Blob object through the window. The URL. CreateObjectURL (Blob) into a URL, then the URL link attribute assigned to a tag, called a.c lick () can be downloaded.

Method and attribute interpretation:

  • Axios.defaults. baseURL: The base path of the request
  • Qs. stringify formatting method

Code examples:

// url: interface address,
// query: request parameter, query format: {testId: XXX, formatId: aaa}
function download(url, query) {

    let xhr = new XMLHttpRequest()
    xhr.open('GET'.`${axios.defaults.baseURL}${url}?${QS.stringify(query)}`.true)
    // If you specify blob as the responseType property during an AJAX request, you download a BLOB object
    xhr.responseType = 'blob'
    xhr.setRequestHeader('Access-Token', storage.session.get('token'))
    xhr.onload = function() {
        if (this.status == 200) {

            let blob = this.response
            / / generated URL
            let href = window.URL.createObjectURL(blob)
            let link = document.createElement('a')
            link.download = PDF ' 'waveform.
            link.href = href
            link.click()
            window.URL.revokeObjectURL(href)
        }
    }
    xhr.send(null)}Copy the code

If base64 data is returned from the back end, it can be converted to a URL using the following method:

     CreateObjectURL creates a temporary URL * for a Blob object@param {String} B64data Base64 Data *@param {String} ContentType Specifies the data type to be converted@param {Number} SliceSize "To Learn" */
    base64ToURL({ b64data = ' ', contentType = ' ', sliceSize = 512 } = {}) {
        return new Promise((resolve, reject) = > {
            // Use the atob() method to decode data
            let byteCharacters = atob(b64data)
            let byteArrays = []
            for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
                let slice = byteCharacters.slice(offset, offset + sliceSize)
                let byteNumbers = []
                for (let i = 0; i < slice.length; i++) {
                    byteNumbers.push(slice.charCodeAt(i))
                }
                // A typed array of 8-bit unsigned integer values. The content will be initialized to 0.
                // If the requested number of bytes cannot be allocated, an exception is thrown.
                byteArrays.push(new Uint8Array(byteNumbers))
            }
            let result = new Blob(byteArrays, {
                type: contentType,
            })
            result = Object.assign(result, {
                CreateObjectURL = createObjectURL
                / / this method generates URL format: blob: http://localhost/c745ef73-ece9-46da-8f66-ebes574789b1
                preview: window.URL.createObjectURL(result),
                name: `XXX.png`,
            })
            resolve(result)
        })
    }
Copy the code

1.2 preview

Here, the pdF.js plug-in is selected for preview.

1.2.1 PDF.js plug-in download

Website address: mozilla. Making. PDF. IO/js/gett…

Browsers Download the stable version of Prebuilt (for Older Browsers). After decompression, build and the Web folder are built. The Web folder is used.

1.2.2 PdF.js plug-in use

The directory structure in which the PDF plug-in resides

(abbreviated) | - / public PDF | | - - build | - web... |- viewer.html ... | - / SRC (abbreviated)Copy the code

Using Vue as an example, other frameworks are used similarly.

Use in xxx.vue components:

Will download after the blob or base64 format file Through the window. The URL. CreateObjectURL () into the URL can be assigned to the pdfURL below.

. <iframe v-if="pdfURL"
       width="100%"
       height="100%"
       scrolling="no"
       :src="`/pdf/web/viewer.html? file=${encodeURIComponent(pdfUrl)}`"
       class="pdf_preview"
></iframe>
...

Copy the code

1.3 print

1.3.1 PDF.js plug-in printing

This method can be printed by clicking the print icon in the header of the pdF.js plug-in

1.3.2 Native method printing

Principle: after will download a Blob object through the window. The URL. CreateObjectURL (Blob) into a URL, the URL assigned to the iframe. SRC, then through the iframe. ContentWindow. Print () to print.

The complete code is as follows:

// url: interface address, query: query parameter
function print(url, query) {
    let xhr = new XMLHttpRequest()

    xhr.open('GET'.`${axios.defaults.baseURL}${url}?${QS.stringify(query)}`.true)
    xhr.responseType = 'blob'
    xhr.setRequestHeader('Access-Token', storage.session.get('token'))
    xhr.onload = function() {
        if (this.status == 200) {
            // let blob = new Blob([this.response], {
            // type: 'application/pdf; charset=utf-8',
            // })
            let blob = this.response
            / / generated URL
            let href = window.URL.createObjectURL(blob)
            // console.log(href)
            Vue.nextTick(() = > {
                let EleIframe = document.querySelector('.iframe_print')
                if (EleIframe) {
                    EleIframe.remove()
                }

                let iframe = document.createElement('iframe')
                iframe.src = href
                iframe.classList.add('iframe_print')
                iframe.style.display = 'none'
                document.body.append(iframe)
                // iframe.contentWindow.focus()
                iframe.contentWindow.print()

                window.URL.revokeObjectURL(href)
            })
        }
    }
    xhr.send()
}
Copy the code

1.3.3 Silent Printing

The principle is to use HTTP or Websocket to communicate with print control when clicking print to realize silent printing. This method I have not tried, interested partners can query information after a try.

2 Electron desktop

2.1 Download to a LOCAL PC

Ideas:

Typically, the back end will return a binary PDF file. In this case, for a native Ajax request, specify xhr.responseType = ‘blob’ to receive the BLOB object. Then FileReader converts the bloB object to base64 and buffer. from(data, ‘base64’) converts base64 to Buffer. ShowSaveDialog and fs.writeFile to download to the specified location.

Method and attribute interpretation:

  • Axios.defaults. baseURL: The base path of the request
  • Qs. stringify formatting method

Code examples:

const { remote } = require('electron')
const dialog = remote.dialog
const fs = require('fs')...// url: interface address,
// query: request parameter, query format: {testId: XXX, formatId: aaa}
function download(url, query) {
    var xhr = new XMLHttpRequest()
    xhr.open('GET'.`${axios.defaults.baseURL}${url}?${QS.stringify(query)}`.true)
    // If you specify blob as the responseType property during an AJAX request, you download a BLOB object
    xhr.responseType = 'blob'
    xhr.onload = function () {
        if (this.status == 200) {
            
            dialog
                .showSaveDialog({
                    title: 'Save file'.defaultPath: `The ${+new Date()}.pdf`.filters: [{ name: '.pdf'.extensions: ['pdf'] }],
                })
                .then((res) = > {
                    if (res.filePath) {
                        let blob = this.response
                        let reader = new FileReader()
                        reader.readAsDataURL(blob)
                        reader.addEventListener('loadend'.function () {
                            // // intercepts the character after base64
                            let data = reader.result.split('base64,') [1]
                            // Convert the truncated characters to Buffer in nodeJS
                            data = Buffer.from(data, 'base64')

                            // Write the file to the manually selected path
                            fs.writeFile(res.filePath, data, 'binary'.(err) = > {
                                if (err) {
                                    console.log(err)
                                } else {
                                    console.log('Saved successfully')
                                }
                            })
                        })
                    }
                })
        } 
    }
    xhr.send(null)}...Copy the code

2.2 preview

Here, the pdF.js plug-in is selected for preview.

2.2.1 PdF.js plug-in download

Website address: mozilla. Making. PDF. IO/js/gett…

Browsers Download the stable version of Prebuilt (for Older Browsers). After decompression, build and the Web folder are built. The Web folder is used.

2.2.2 Use pdF.js plug-in

The directory structure in which the PDF plug-in resides

(abbreviated) | - / public PDF | | - - build | - web... |- viewer.html ... | - / SRC (abbreviated)Copy the code

Download the PDF file to the specified directory. For example, write the PDF file to the /public/data directory

WriteFile. Js file:

const {  remote } = require('electron')
const app = remote.app
const fs = require('fs')
const path = require('path')...// Called when previewing the PDF
// url: interface address,
// query: request parameter, query format: {testId: XXX, formatId: aaa}
function downloadPreview(url, query) {
    return new Promise((resolve, reject) = > {
        var xhr = new XMLHttpRequest()
        xhr.open('GET'.`${axios.defaults.baseURL}${url}?${QS.stringify(query)}`.true)
        xhr.responseType = 'blob'
        xhr.setRequestHeader('Access-Token', sessionStorage.getItem('token'))
        xhr.onload = function () {
            if (this.status == 200) {
                // console.log(this.response)
                let blob = this.response
                let reader = new FileReader()
                reader.readAsDataURL(blob)
                reader.addEventListener('loadend'.function () {
                    // // intercepts the character after base64
                    let data = reader.result.split('base64,') [1]
                    // Convert the truncated characters to Buffer in nodeJS
                    data = Buffer.from(data, 'base64')

                    let filePath
                    // Development environment
                    if (process.env.NODE_ENV === 'development') {
                        filePath = path.join(__static, 'data/preview.pdf')
                        // Production environment
                    } else {
                        filePath = path.join(app.getAppPath(), 'data/preview.pdf')
                    }
                    fs.stat(filePath, (err, stats) = > {
                        if(! err) {// console.log(stats);
                            // console.log(stats.isFile());
                            // If the file exists, delete it before writing it
                            if (stats.isFile()) {
                                let res = fs.unlinkSync(filePath)
                                if(! res) { fs.writeFile(filePath, data,'binary'.(err) = > {
                                        if (err) {
                                            // console.log(err)
                                            reject(err)
                                        } else {
                                            // console.log(' saved successfully ')
                                            resolve('.. /.. /data/preview.pdf')}})}}}else {
                            // Write directly if the file does not exist
                            fs.writeFile(filePath, data, 'binary'.(err) = > {
                                if (err) {
                                    // console.log(err)
                                    reject(err)
                                } else {
                                    // console.log(' saved successfully ')
                                    resolve('.. /.. /data/preview.pdf')
                                }
                            })
                        }
                    })
                })
            } 
        }
        xhr.send(null)})}...export {downloadPreview}
Copy the code

About potholes in file writing paths in development and production environments:

  • path.join(__static, 'data/preview.pdf')

    C:\Program Files (x86)\lotus\ Resources \app.asar\data\preview.pdf

    Production values: C:\Users\DELL\Desktop\lotus_plus\public\data\preview

  • app.getAppPath()

    Values in the development environment: C:\Users\DELL\Desktop\lotus_plus\dist_electron

    Value in production: C: Program Files (x86) lotus resources app

To find the public/data/preview. PDF file in the project structure, do the following

let filePath
 // Development environment
 if (process.env.NODE_ENV === 'development') {
      filePath = path.join(__static, 'data/preview.pdf')
 // Production environment
    } else {
      filePath = path.join(app.getAppPath(), 'data/preview.pdf')}Copy the code

Using Vue as an example, other frameworks are used similarly.

Use in xxx.vue components:

. <iframe v-if="pdfUrl"
       width="100%"
       height="100%"
       scrolling="no"
       :src="`/pdf/web/viewer.html? file=${encodeURIComponent(pdfUrl)}`"
       class="pdf_preview"
></iframe>
...
import { downloadPreview } from '@/xxx/writeFile.js'.// Assign the path of the PDF file to the pdfUrl to preview the downloaded PDF file
downloadPreview('xxx/xxx', {id: xxx}).then(res= > {this.pdfUrl = res})
...
Copy the code

2.3 print

2.3.1 PDF.js plug-in printing

This method can be printed by clicking the print icon in the header of the pdF.js plug-in

2.3.2 Printing by native method

Refer to section 1.3.2

2.3.3 Silent Printing

Principle: Write to the specified filePath via fs.writeFile, and then run the SumatraPDF plug-in to print the buffer using the childProcess module of NodeJS. Delete the downloaded TEMPORARY PDF file from fs.unlink.

The js code in the Vue component is as follows:

This.$api.exportReport is an HTTP request, blob is the blob object corresponding to the returned PDF, and IPC. Send (‘printPDF’, filePath) is the way the renderer communicates with the main process.

 const fs = require('fs')
const path = require('path')
import { Buffer } from 'buffer'.this.$api.exportReport(
                    { testId: item.testId, formatId },
                    (blob) = > {
                        let reader = new FileReader()
                        reader.readAsDataURL(blob)
                        reader.addEventListener('loadend'.() = > {
                            // Intercepts the character after base64
                            let data = reader.result.split('base64,') [1]
                            // Convert the truncated characters to Buffer in nodeJS
                            data = Buffer.from(data, 'base64')
                            // console.log(data);

                            let filePath
                            // Development environment
                            if (process.env.NODE_ENV === 'development') {
                                filePath = path.join(__static, `data/${index}.pdf`)
                                // Production environment
                            } else {
                                filePath = path.join(app.getAppPath(), `data/${index}.pdf`)
                            }

                            fs.writeFile(filePath, data, 'binary'.(err) = > {
                                if (err) {
                                    console.log(err)
                                } else {
                                    console.log('Saved successfully')
                                    ipc.send('printPDF', filePath)
                                }
                            })
                        })
                    },
                    'blob')...Copy the code

The main process code is as follows:

ipc.on('printPDF'.(e, filePath) = > {
    switch (process.platform) {
        case 'darwin':
        case 'linux':
            childProcess.exec(
                `SumatraPDF.exe -print-to-default  "${filePath}"`,
                {
                    windowsHide: true.cwd: path.join(__static, 'SumatraPDF'),},(e) = > {
                    if (e) {
                        throw e
                    }
                    /* Delete the created temporary file */ after printing
                    fs.unlink(filePath, (e) = >{})})break
        case 'win32':
            // child_process.exec(command[, options][, callback])
            // command 
      
       : Command to run, separated by Spaces.
      
            // options.
            / / CWD < string > | < URL > child of the current working directory. Default: process.cwd().
            // windowsHide < Boolean > Hides child process console Windows that are normally created on Windows systems. Default value: false.
            childProcess.exec(
                `SumatraPDF.exe -print-to-default  "${filePath}"`,
                {
                    windowsHide: true.cwd: path.join(__static, 'SumatraPDF'),},(e) = > {
                    if (e) {
                        throw e
                    }

                    /* Delete the created temporary file */ after printing
                    fs.unlink(filePath, (e) = >{})})break
        default:
            throw new Error('Platform not supported.')}})Copy the code

Due to my limited level, if there are any mistakes in this article, please correct them in the comments section. If the article has some help for you, welcome to like and pay attention to!