10

I have a canvas that you can paint on. I need to save it's contents to the server so it can be resumed later.

To do this, I xMLHttpReq.send(*) the encodeURIComponent(canvasP.toDataURL())* via a xMLHttpReq.open("POST", url, false);

In my php script, I take the $_POST and base64_decode(json_decode($_POST[data])) and I save it to a .png file on the server.

NOW, this works fine and dandy if the canvas has a small doodle on it. BUT if the 1600x1200px canvas has a lot of stuff drawn on it, and it's like 600kB in size, my script fails, and I get a 0kB png on my server.

How do I fix this? How can I send a large canvas to the server?

Is the problem a limitation to _POST size? Because, no matter how large the canvas is, I can always download it via a window.open(canvasP.toDataURL(), "blabla");.

This is quite the dilemma for me. Any help will be much appreciated!

[EDIT] The PHP simply receives empty $_POST[data] for anything over ~50-something kB... So the problem has to occur between canvasP.toDataURL() and the POST-ing...

[EDIT2] Browser says "Uploading...(xx%)" until it's "done". SO WHERE DOES THE DATA GO? :C

[EDIT3] Here's the app, try it for yourself (use google chrome): http://students.info.uaic.ro/~tudor.berechet/

[EDIT4] Mike's answer seems to be right about suhosin phpinfo screenshot

Spectraljump
  • 4,189
  • 10
  • 40
  • 55
  • 2
    What message does it fail with on the PHP end? Sounds like a memory issue in the script... – Pekka Feb 20 '11 at 17:31
  • 3
    What's your server's `post_max_size`? What's the actual size of the URI encoded data being sent to the server? – sdleihssirhc Feb 20 '11 at 17:32
  • I don't know how could I check anything on the server side, as I do not own the server and I have no control over it other than FTP access. Sdleihssirhc, so you're saying the server states how long a _Post message can be? I downloaded 2 images, 495kB and 632kB, that failed on server. – Spectraljump Feb 20 '11 at 17:48
  • 2
    You may not be able to modify preferences, but you always can read them from php: `ini_get('post_max_size')` – kirilloid Feb 20 '11 at 18:02
  • @kirilloid I should have thought of that. I did that and it says "12M". So I guess this isn't the problem... – Spectraljump Feb 20 '11 at 18:05
  • This is almost certainly either a problem with your network connection timing out (which seems unlikely if your "Edit 2" means that you're reaching 100%) or more probably an issue with your PHP script. I've added the `php` tag to see if you can get help from people with that skill set. – Phrogz Feb 20 '11 at 21:41
  • I'd like to test it, but I can't figure out what to do, using Safari 5. Where is the canvas that I can paint on? If you open the Developer Tools in Safari/Chrome and watch your post request, you should see that all the data is being sent. – Phrogz Feb 20 '11 at 21:44
  • 1
    You will need to try and get a reaction from the PHP script. There is bound to be a response body somewhere. My bet is it contains PHP errors. – Pekka Feb 20 '11 at 21:45

1 Answers1

10

It sounds very much like your php was compiled with Suhosin. The default limit of length for any post variable with Suhosin is 65000 bytes which is quite close to what you are estimating as your limit.

In fact, your server is sending the X-Powered-By header with a value of PHP/5.2.6-1+lenny9. I googled this package version and on the Debian website they mention it was built with Suhosin.

Since you do not have control over your server configuration, the solution would be to split up the canvas data into multiple variables below the post length limit of your server and then reassemble in PHP. If you check your phpinfo() it should show all these limits.

Edit - Added example code

Javascript:

var canvasData = canvasP.toDataURL(); 
var length = canvasData.length;
var content = '';
var index = 0;
var limit = 64999;
var l;
while (length > 0) {
    if (length <= limit)
        l = length;
    else
        l = limit;

    content += '&content[]=' + canvasData.substr(index * limit, l);
    length -= l;
    index++;
}

xhr.send(content);

I don't believe you need the encodeURIComponent() you have because toDataURL() encodes as base64 which is url safe.

PHP:

if (!empty($_POST['content']) && is_array($_POST['content']))
{
    $content = '';
    foreach ($_POST['content'] as $part)
    {
        $content .= $part;
    }
    $content = base64_decode($content);
}

Not the most efficient method to do this, but it may help you.

This will still have it's limits with Suhosin, but you will be able to send much more data this way. Looks like you will be limited to 64 parts of 65000 bytes in an array before you will also have to use multiple arrays. However, at that point it will already getting way too large for most people to be uploading often and it would probably be better to send the changes to the image somehow instead of the whole image.

Mike
  • 903
  • 6
  • 17
  • Thank you sir for figuring it out. Not very experienced with server-side stuff myself. So now I have to divide the string into <65kB pieces and do multiple POST-s... It seems like a lot of trouble to try to piece them all together on the php side. I'd have to add "headers" to each post so the script will know what to do with the pieces. Is there no other way to save your stuff on a server? Like some sort of reverse streaming? – Spectraljump Feb 21 '11 at 08:54
  • Interesting... never thought, there may be limit from php build. – kirilloid Feb 21 '11 at 18:06
  • I'm not sure of any easier way you could send the data besides POSTing it, but you don't necessarily have to do multiple POSTs. You just have to send multiple variables with the single POST having each variable being under 65000 bytes. And as long as the total of all values is below your limit of `post_max_size`, you should be fine. I've updated my answer for a possible method you could use to do this, it is untested however so I'm not sure if it will work. And of course, it would be a lot better if you could just get some hosting without Suhosin installed, but that's not always possible. – Mike Feb 21 '11 at 20:46
  • Now I get it! I had thought suhosin overwrites the 12M limit to post_max_size. Thanks a lot for the example code as well. I'll definitely get it to work now. I was using `encodeURIComponent()` because besides the canvas content, I was also appending a different variable to the sent data, which had an URL in it... – Spectraljump Feb 22 '11 at 09:18
  • @Myself [Edit] Just so everyone knows, `encodeURIComponent(your_base64)` seems to be required. Without the URI encoding, it doesn't work. In other words, base64 seems to not be _POST safe. – Spectraljump Feb 24 '11 at 10:52