1

Below code does save a file to the user's disk:

function handleSaveImg(event){
  const image = canvas.toDataURL();
  const saveImg = document.createElement('a');
  saveImg.href = image;
  saveImg.download= saveAs;
  saveImg.click();
}

if(saveMode){
  saveMode.addEventListener("click", handleSaveImg);
}

It uses an <a> tag to save some data (in my case, an image exported from a <canvas>).

But this saves directly to the disk, with no prompt asking where to save the file, nor under which name.

I want to force the displaying of the "Save as" dialog box, so that the user has to choose where they'll save that file.
Is there any way?

Kaiido
  • 123,334
  • 13
  • 219
  • 285
kkokki
  • 51
  • 1
  • 4
  • Does this answer your question? [How to make a browser display a "save as dialog" so the user can save the content of a string to a file on his system?](https://stackoverflow.com/questions/11336663/how-to-make-a-browser-display-a-save-as-dialog-so-the-user-can-save-the-conten) –  Jun 02 '21 at 11:40
  • Does this answer your question? [Using HTML5/JavaScript to generate and save a file](https://stackoverflow.com/questions/2897619/using-html5-javascript-to-generate-and-save-a-file) – oligofren Jun 02 '21 at 11:40

2 Answers2

11

Yes, and it's called showSaveFilePicker().

This is part of the File System Access API, which is still a draft, but is already exposed in all Chromium browsers.

This API is quite powerful and will give your code direct access to the user's disk, so it is only available in secure contexts.

Once the Promise returned by this method resolve, you'll get access to an handle where you'll be able to access a WritableStream to which you'll be able to write your data.

It's a bit more complicated than download, but it's also a lot more powerful, since you can write as stream, not needing to have the whole data in memory (think recording a video).

In your case this would give

async function handleSaveImg(event){
  const image = await new Promise( (res) => canvas.toBlob( res ) );
  if( window.showSaveFilePicker ) {
    const handle = await showSaveFilePicker();
    const writable = await handle.createWritable();
    await writable.write( image );
    writable.close();
  }
  else {
    const saveImg = document.createElement( "a" );
    saveImg.href = URL.createObjectURL( image );
    saveImg.download= "image.png";
    saveImg.click();
    setTimeout(() => URL.revokeObjectURL( saveImg.href ), 60000 );
  }
}

Here is a live demo, (and the code).

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • Wow it's amazing! when I saw live demo and code, I want to be in a hurry to adapt mycode to yours but as a beginner, I think it will take such time. Thank you for detailed answer. – kkokki Jun 02 '21 at 14:26
  • 1
    I adapt your code and it works! I'm still studying asynce and await... but very happy to learn new thing! This is my github page you can look what I made. I literally copy and paste yours... https://github.com/kokiok3/vanilaJs-canvasUpgrade – kkokki Jun 22 '21 at 07:26
  • this works extremely well; you are a legend – Matt Oct 07 '22 at 17:00
  • 2
    This is great, but has very limited [browser support](https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker#browser_compatibility) at the moment. – djvg Nov 03 '22 at 11:09
0

No.

You can specify a filename by assigning a string to the download property.

There is no way to persuade a browser to show a SaveAs dialog when it is configured to save downloads to a default folder without prompting. That is entirely under the control of the user.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • Then it means if there is no default folder I can make SaveAs dialog? I am confusing that your answer is completely "NO" and "No way". – kkokki Jun 02 '21 at 11:53
  • 1
    You can set up a download. You have done this. The browser might save it to a default folder **or** display a Save As dialog but **you** (the developer of the webpage) **cannot** control which. – Quentin Jun 02 '21 at 12:27