6

I'm making an export function to a HTML5 game of mine and my current saving method is a crude serialization of game data and then:

// this is Javascript
var gameData = "abc"; // this is actually a HUGE string of over 2MB
try
{
    document.location = "data:text/octet-stream,"+encodeURIComponent(JSON.stringify(gameData));
}
catch(e)
{
    console.log(e);
}

From: Using HTML5/Javascript to generate and save a file

I don't mind the fact that I can't use it for big strings, but I'd like it to generate a warning that informs that this method doesn't work, unfortunately Chrome (16) crashes without catching that exception.

Is there a better way to implement this kind of export, the important thing being for me is to make it work locally. FileAPI would be a better solution, but doesn't work locally.

Community
  • 1
  • 1
Solenoid
  • 2,351
  • 5
  • 29
  • 48
  • 1
    What is crashing exactly? `document.location =` or `encodeURI`? – rds Jan 05 '12 at 19:26
  • Where do you want to export to? Server side? Only save client-side? – rds Jan 05 '12 at 19:27
  • 1
    Clearly he wants to export client-side. And what is crashing is document.location – Ivan Castellanos Jan 05 '12 at 19:28
  • Why not use client side storage instead of this at best hack? Are you worried about the user clearing the data? – scottheckel Jan 05 '12 at 19:29
  • Why not use the `localStorage` object? `localStorage.setItem('gameData',JSON.stringify(gameData));` Then get it back with `gameData = JSON.parse(localStorage.getItem('gameData'));` – Matt H Jan 05 '12 at 19:36
  • If your `gameData` object is already a string why are you then encoding it as JSON with `JSON.stringify()`? As @rds asks, at what point exactly is Chrome bailing? – Jordan Running Jan 05 '12 at 19:49
  • I already use `localStorage` for fast cache (lots of swapping going on), but if the user wants to export the save file then this is required. The crashing occurs at `document.location`, just tested without `encodeURI`. `gameData` is actually a huge object. – Solenoid Jan 05 '12 at 20:20
  • I really don't see a way forward except to fix your `gameData` implementation. Perhaps post on programmers.stackexchange and see if anyone there has pointers on how to serialize game state in a sane way. Dumping "a huge object" into a string is going to keep making things difficult for you and for the user in the long run. – Jordan Running Jan 05 '12 at 21:18
  • Might be related to [issue 85174](http://code.google.com/p/chromium/issues/detail?id=85174) – rds Jan 05 '12 at 23:40

2 Answers2

1

AFAIK not possible client-side; but a 1.99MB file can be saved this way in Chrome; maybe you should try to compress/optimize your game data a little bit. One way to do that is to use JSZip.

In order to check if the current browser is Google Chrome and so the method doesn't work with long strings you can use something like this:

if(gameData.length > 1999999 && window.chrome){
    alert("Sorry, this export method does not work in Google Chrome")
    return; 
}
Ivan Castellanos
  • 8,041
  • 1
  • 47
  • 42
  • I don't mind that it doesn't work, I know how to make the save file smaller, but I don't want it to crash and I don't know any other method than `try/catch`. – Solenoid Jan 05 '12 at 20:25
  • 4
    Chrome is crashing at a a level deeper than your JavaScript code. You can't catch this error; you can only do your best to avoid the circumstances that cause it. – Jordan Running Jan 05 '12 at 21:29
0

I assume that the json.stringify is working But that the win.location fail due to the fact that the URI is to big.

What you can do is to convert your uri to a blob, and then

URL.createObjectURL(file)

This will create a url that point to an internal object (internal to the browser).

And it will work as expected.

Here is a a method to convert dataURI to blob

function(dataURI) {
  // convert base64 to raw binary data held in a string
  // doesn't handle URLEncoded DataURIs
  var byteString;
  if (dataURI.split(',')[0].indexOf('base64') >= 0)
      byteString = atob(dataURI.split(',')[1]);
  else
      byteString = unescape(dataURI.split(',')[1]);
  // separate out the mime component
  var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

  // write the bytes of the string to an ArrayBuffer
  var ab = new ArrayBuffer(byteString.length);
  var ia = new Uint8Array(ab);
  for (var i = 0; i < byteString.length; i++) {
      ia[i] = byteString.charCodeAt(i);
  }

  // write the ArrayBuffer to a blob, and you're done
  return new Blob([ab],{type: mimeString});
}

}

benzen
  • 6,204
  • 4
  • 25
  • 37