“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!