Search Unity

Problems saving files from WebGL via JavaScript. Conversion of byte-array?

Discussion in 'Web' started by Jochen_Winkler_at_Flavia, Oct 11, 2019.

  1. Jochen_Winkler_at_Flavia

    Jochen_Winkler_at_Flavia

    Joined:
    Sep 13, 2019
    Posts:
    4
    I am working on an App that can run in WebGL, but should be able to save files (png, pdf, other) on the user's hard-drive.
    Using a small php-script on the server is not a good option in this case (customer would have to take care of it, end-user would need internet-connection to save a file).

    There is a possibility by using an external JavaScript, as outlined here.
    It uses Unity's ability to interact with browser-scripting and this download-script programmed in JavaScript.

    I was able to save simple text-files this way. But when I try saving a screenshot as png, I get problems: Unity seems to be able to send strings, numbers and some pointers to Javascript, but the download-script accepts string, "File", url and "Blob".


    I tried using Base 64 String. In C#:
    Code (CSharp):
    1.  
    2. [DllImport("__Internal")]
    3. private static extern void download(string data, string strFileName, string strMimeType);
    4. public void ExportFromWeb()
    5. {
    6.     byte[] bytes = ToTexture2D(renderTexture).EncodeToPNG();
    7.     string bytesAsString = Convert.ToBase64String(bytes);
    8.     download(bytesAsString, "downloadPng.png", "image/png");
    9. }
    10.  
    Then changed the jslib slightly:
    Code (JavaScript):
    1.  
    2. mergeInto(LibraryManager.library, {
    3.   download: function (my_data, my_strFileName, my_strMimeType) {
    4.     var data = Pointer_stringify(my_data);
    5.     var strFileName = Pointer_stringify(my_strFileName);
    6.     var strMimeType = Pointer_stringify(my_strMimeType);
    7. ...
    8.  
    It saves a file of a sensible size, but when I try to open it I get "Not a PNG file".

    Some other things I looked into:
    • I guessed using a Blob would be the correct thing to do for a big amount of binary data. But I did not find anything useful when it comes to Blob and Unity.
    • The Unity documentation shows how to send an array, so I tried to send the pointer to the byte[]-array and recreate it in javascript. Sadly, I did not get better results with that.
    • Since the download-script claims to accept "Files", I searched if I could create a "File" without interacting with the user's computer. I found nothing so far.


    I realize this is a pretty open question - But I would be happy for any leads or hints you could give me.
     
  2. sumpfkraut

    sumpfkraut

    Joined:
    Jan 18, 2013
    Posts:
    242
  3. Jochen_Winkler_at_Flavia

    Jochen_Winkler_at_Flavia

    Joined:
    Sep 13, 2019
    Posts:
    4
    Ah! There are many solutions in the thread you linked. I used the one in this post here.
    It works quite well! Thank you!

    (Edit:

    I had to do one minor change by removing/commenting the last line of the Java-Script. Also changed the position of the "mergeInto". Looks like this now:


    Code (JavaScript):
    1. mergeInto(
    2.   LibraryManager.library,
    3.   {
    4.     DownloadFile : function(array, size, fileNamePtr)
    5.     {
    6.       var fileName = UTF8ToString(fileNamePtr);
    7.    
    8.       var bytes = new Uint8Array(size);
    9.       for (var i = 0; i < size; i++)
    10.       {
    11.           bytes[i] = HEAPU8[array + i];
    12.       }
    13.    
    14.       var blob = new Blob([bytes]);
    15.       var link = document.createElement('a');
    16.       link.href = window.URL.createObjectURL(blob);
    17.       link.download = fileName;
    18.    
    19.       var event = document.createEvent("MouseEvents");
    20.       event.initMouseEvent("click");
    21.       link.dispatchEvent(event);
    22.       window.URL.revokeObjectURL(link.href);
    23.       //link.parentNode.removeChild(link);
    24.     }
    25.   }
    26. );
    Saved it as Plugins/JavaScript/DownloadFile.jslib .

    For completion (if our links should stop working one day):

    Added the following to my C#-file
    Code (CSharp):
    1.  [DllImport("__Internal")] private static extern void DownloadFile(byte[] array, int byteLength, string fileName);
    Then I could call it like a normal function.
     
    Last edited: Oct 14, 2019