Search code examples
javascript

Paste multiple files from clipboard not working


So i noticed everytime i paste some file, if i have more than 1 file on my clipboard area it only pastes 1 file, the others my code just don't process.

Here is the snippet to reproduce, even if i have more than 1 file on clipboard it only inserts 1 item in the array. I'm trying to figure out what is happening. (use image files to test).

const inputFileToBase64 = file => new Promise((resolve, reject) => {
  const reader = new FileReader()
  reader.readAsDataURL(file)

  reader.onload = () => resolve(reader.result)
  reader.onerror = error => reject(error)
})

const getBase64Files = async (items) => {
  const base64Files = []  
  
  for (const item of items) {  
     const base64 = await inputFileToBase64(item.getAsFile())
     
     base64Files.push(base64)
  }
  
  return base64Files
}

document.addEventListener('paste', async (event) => { 
  console.log('pasted')
  const items = event.clipboardData.items
  const files = await getBase64Files(items)
  console.log(files)
})
PASTE HERE


Solution

  • The DataTransferItemList items seems to be consumed as soon as the event loop allows it to; it doesn't wait for a FileReader to read it. So, by the time the first file is read, items is empty.

    If you allow the task queue to do its stuff, even with a setTimeout of 0, you lose all files:

      const items = event.clipboardData.items
      await new Promise(resolve => setTimeout(resolve, 0)) //wait 0ms
      const files = await getBase64Files(items) //no items, no files
    

    and that's also happening with the working version of getBase64Files below.

    Regardless of the explanation (which one might improve upon), it is always a good idea to start all reading at the same time and await for all to complete:

    const getBase64Files = async (items) => {
      const base64Files = []
    
      await Promise.allSettled(
        Array.from(items).map(
          item => inputFileToBase64(item.getAsFile()).then(
            file => base64Files.push(file)
          )
        )
      )
       
      return base64Files
    }
    

    and that works.