in reply to Sending file as "TEXT" or "BLOB"

Communicating with headers is OK but it can be more versatile if you implement your own communication protocol. Simply because you can as you are in total control of both server and client code. So, why not make your communication a bit more verbose and standard by always including 1) operation-status, 2) text message, 3) optional html message, 4) optional JavaScript code to execute (just joking), 5) optional data. All wrapped as JSON hash for example.

Client-to-server communication via sending an XHR (XMLHttpRequest) (generally: AJAX) works on the "same page". No need to reload or move to a new page. The client sends a request to the server which responds with running a script (in your case "download.pl") or routing to a controller, etc. The results are passed back to the caller, i.e. the client's browser and notify your XMLHttpRequest object (your xhhttp) by means of activating callbacks you implemented when you created the object. That's provided the client did not move to a new page or reloaded!

You can implement a number of different callbacks or you can implement just one, onreadystatechange just as you did in your code. This function will be called each time the communication changes a state, and accompanied with appropriate data. And so it can, for example, report on progress, bytes transfered etc, report a server error (500,404) or process the received data if server did send something without complaining. See https://javascript.info/xmlhttprequest for more details.

The callback can then notify the user by *dynamically* creating or changing an HTML element, e.g. a text box's content or popping up a message. This is well documented on the internet.

So, what I suggest is you keep your callback handler mostly unchanged. On state=4 (XHR completed), as you now do, check the server response and alert the user if it was not 200/OK. If it was, then unwrap the "complex" data you received from server in order to check its status, e.g. file-too-big, permission-denied, no-such-file-do-you-want-to-create, (an arbitrarily long list!) etc. And also present the user with the error message/instructions the server sent you. And/or process the data part and decode your image. Which, btw, must be text-encoded (e.g. via base64).

On the server side it looks like this:

... use JSON; use MIME::Base64; my $ret_data = { 'status' => 5, # success 'message' => "here is the file $filename you asked" 'message-html' => "<b>success blah blah for $fileame</b>", 'data' => MIME::Base64::encode_base64($image_binary_data) }; # if using CGI.pm use CGI; my $cgi = CGI->new; print $cgi->header(-type => "application/json", -charset => "utf-8"); print JSON::encode_json($ret_data); # or if using Mojolicious $c->render(json => $ret_data);

1 minute Edit: here is the data I would send on a failure, e.g. file permissions or file-too-big-to-send, or ... One advantage of this method is that you can still send data even on errors.

my $ret_data = { 'status' => -1, # file was too big and I truncated it 'message' => "here is the file $filename you asked but it is truncat +ed" 'message-html' => "<b>success blah blah for $fileame but truncated</ +b>", 'data' => truncate($data) };

bw, bliako