How Can I Save An Image To The User's Filesystem In A Web Export?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By ChimpCEO

I have a project that where I want the user to be able to save a screenshot in-game to their filesystem.

Here’s how I generate the screenshot image:

get_viewport().set_clear_mode(Viewport.CLEAR_MODE_ONLY_NEXT_FRAME)
yield(VisualServer, "frame_post_draw")
var img = self.get_viewport().get_texture().get_data()
img.flip_y()
img.save_png("res://screenshot.png")

When the project is exported as a desktop app (tested on windows), this works just fine and saves the generated image to the project’s directory.

However, in a web export, this doesn’t work. Nothing is prompted for the user to save the image.

I tried a work around where it’d open the image in a new tab so the user could save it from there, using the below code:

var url = "data:image/png;base64," + Marshalls.raw_to_base64(img.data.data)
OS.shell_open(url)

But this doesn’t work. Godot’s OS.shell_open() doesn’t seem to recognize data:image/png;base64, as a link to open using the web browser.

Is there anyway for me to save an image from a Godot project to the user’s filesystem (either directly or by popping up a file-save prompt or by opening the image in a new tab) in a web exported build?

Try using this for Javascript downloads.

exuin | 2021-05-12 21:11

Oh, this is great! Thanks for the link

ChimpCEO | 2021-05-12 22:53

:bust_in_silhouette: Reply From: Calinou

Is there anyway for me to save an image from a Godot project to the user’s filesystem (either directly or by popping up a file-save prompt or by opening the image in a new tab) in a web exported build?

Web browsers do not allow this for security reasons. You need to trigger a file download using JavaScript code (that can be called from GDScript) instead.

There is an experimental HTML5 Filesystem API that would allow websites to save files to arbitrary locations with the user’s permission, but it’s not widely supported and Godot doesn’t implement it yet.

PS: Do not write files to res:// in an exported project, as the project directory should not assumed to be writable to. user:// should be used to save user data instead.

Thanks! I remember reading about the user:// path but was just sticking with res:// in development.

Thanks for the clarification :slight_smile:

ChimpCEO | 2021-05-12 22:52

:bust_in_silhouette: Reply From: BerlinNights

Godot 3.4
1.Version: Accessing buffer directly from Image class

func Download_File(_img,_filename):
    var buf = _img.save_png_to_buffer()
    JavaScript.download_buffer(buf,_filename+".png")

2.Version: Accessing buffer from File class, as mentioned by Calinou here:

func Save_PNG(_img,_path,_filename):
    var _err = _img.save_png(_path+"/"+_filename+".png")
    Download_File(_path+"/"+_filename+".png",_filename)
    #Put Error Handling Here

func Download_File(_path,_filename):
    var f = File.new()
    f.open(_path,File.READ)
    var buf = f.get_buffer(f.get_len())
    JavaScript.download_buffer(buf,_filename+".png")
    f.close()