mirror of
				https://github.com/1disk/edp445.git
				synced 2024-08-14 22:47:02 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			103 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			103 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const fs = require('fs')
 | |
| const { Image } = require('./js-binding')
 | |
| const { Readable } = require('stream')
 | |
| 
 | |
| let http, https
 | |
| 
 | |
| const MAX_REDIRECTS = 20,
 | |
|   REDIRECT_STATUSES = new Set([301, 302]),
 | |
|   DATA_URI = /^\s*data:/
 | |
| 
 | |
| /**
 | |
|  * Loads the given source into canvas Image
 | |
|  * @param {string|URL|Image|Buffer} source The image source to be loaded
 | |
|  * @param {object} options Options passed to the loader
 | |
|  */
 | |
| module.exports = async function loadImage(source, options = {}) {
 | |
|   // load readable stream as image
 | |
|   if (source instanceof Readable) return createImage(await consumeStream(source), options.alt)
 | |
|   // use the same buffer without copying if the source is a buffer
 | |
|   if (Buffer.isBuffer(source)) return createImage(source, options.alt)
 | |
|   // construct a buffer if the source is buffer-like
 | |
|   if (isBufferLike(source)) return createImage(Buffer.from(source), options.alt)
 | |
|   // if the source is Image instance, copy the image src to new image
 | |
|   if (source instanceof Image) return createImage(source.src, options.alt)
 | |
|   // if source is string and in data uri format, construct image using data uri
 | |
|   if (typeof source === 'string' && DATA_URI.test(source)) {
 | |
|     const commaIdx = source.indexOf(',')
 | |
|     const encoding = source.lastIndexOf('base64', commaIdx) < 0 ? 'utf-8' : 'base64'
 | |
|     const data = Buffer.from(source.slice(commaIdx + 1), encoding)
 | |
|     return createImage(data, options.alt)
 | |
|   }
 | |
|   // if source is a string or URL instance
 | |
|   if (typeof source === 'string' || source instanceof URL) {
 | |
|     // if the source exists as a file, construct image from that file
 | |
|     if (fs.existsSync(source)) {
 | |
|       return createImage(await fs.promises.readFile(source), options.alt)
 | |
|     } else {
 | |
|       // the source is a remote url here
 | |
|       source = !(source instanceof URL) ? new URL(source) : source
 | |
|       // attempt to download the remote source and construct image
 | |
|       const data = await new Promise((resolve, reject) =>
 | |
|         makeRequest(
 | |
|           source,
 | |
|           resolve,
 | |
|           reject,
 | |
|           typeof options.maxRedirects === 'number' && options.maxRedirects >= 0 ? options.maxRedirects : MAX_REDIRECTS,
 | |
|           options.requestOptions,
 | |
|         ),
 | |
|       )
 | |
|       return createImage(data, options.alt)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // throw error as dont support that source
 | |
|   throw new TypeError('unsupported image source')
 | |
| }
 | |
| 
 | |
| function makeRequest(url, resolve, reject, redirectCount, requestOptions) {
 | |
|   const isHttps = url.protocol === 'https:'
 | |
|   // lazy load the lib
 | |
|   const lib = isHttps ? (!https ? (https = require('https')) : https) : !http ? (http = require('http')) : http
 | |
| 
 | |
|   lib
 | |
|     .get(url, requestOptions || {}, (res) => {
 | |
|       const shouldRedirect = REDIRECT_STATUSES.has(res.statusCode) && typeof res.headers.location === 'string'
 | |
|       if (shouldRedirect && redirectCount > 0)
 | |
|         return makeRequest(new URL(res.headers.location), resolve, reject, redirectCount - 1, requestOptions)
 | |
|       if (typeof res.statusCode === 'number' && (res.statusCode < 200 || res.statusCode >= 300)) {
 | |
|         return reject(new Error(`remote source rejected with status code ${res.statusCode}`))
 | |
|       }
 | |
| 
 | |
|       consumeStream(res).then(resolve, reject)
 | |
|     })
 | |
|     .on('error', reject)
 | |
| }
 | |
| 
 | |
| // use stream/consumers in the future?
 | |
| function consumeStream(res) {
 | |
|   return new Promise((resolve, reject) => {
 | |
|     const chunks = []
 | |
| 
 | |
|     res.on('data', (chunk) => chunks.push(chunk))
 | |
|     res.on('end', () => resolve(Buffer.concat(chunks)))
 | |
|     res.on('error', reject)
 | |
|   })
 | |
| }
 | |
| 
 | |
| function createImage(src, alt) {
 | |
|   const image = new Image()
 | |
|   image.src = src
 | |
|   if (typeof alt === 'string') image.alt = alt
 | |
|   return image
 | |
| }
 | |
| 
 | |
| function isBufferLike(src) {
 | |
|   return (
 | |
|     (src && src.type === 'Buffer') ||
 | |
|     Array.isArray(src) ||
 | |
|     src instanceof ArrayBuffer ||
 | |
|     src instanceof SharedArrayBuffer ||
 | |
|     src instanceof Object.getPrototypeOf(Uint8Array)
 | |
|   )
 | |
| }
 |