Outputting file contents as binary as AJAX response - php

OK the title doesn't give much a way so let me explain my very strange set-up.
Two servers are involved:
website: remote
localhost: local machine
The workflow is as follows:
The site calls localhost via cross-domain AJAX
In response localhost dynamically creates a ZIP file via PHP's ZipArchive lib
localhost conveys the raw data that comprises the archive as the AJAX response
The request is made and the archive is made - all good. The archive is openable, all good. What I'm stuck on now is how to convey that archive as the AJAX response, such that it can be "put together again" (à la Humpty Dumpty). When I do this currently (via file_put_contents()) it errors on opening, saying it's invalid. Currently I'm just outputting the archive's raw data:
echo file_get_contents('path/to/archive.zip');
This is fine, but sends garbled characters in the response. I don't know much about encoding and headers, so apologise if this seems obvious.
For the response, should I be looking to convert it to binary, or sending certain headers etc? I tried sending the multipart/form-data header, but no dice. Headers aren't my strong point.
Please note cURL is not an option in this scenario, else I'd be laughing.

You have to read the zip file as a binary data with Blob javascript class.
This is a code snippet from Mozilla documentation
var oReq = new XMLHttpRequest();
oReq.open("GET", "/myfile.png", true);
oReq.responseType = "arraybuffer";
oReq.onload = function(oEvent) {
var blob = new Blob([oReq.response], {type: "application/octet-stream"}); //
// you have nothing to do with the blob...
// ...
};
oReq.send();
Then send this file (blob) with POST method to your destination
var oReq = new XMLHttpRequest();
oReq.open("POST", url, true);
oReq.onload = function (oEvent) {
// Uploaded.
};
oReq.send(blob); //the blob that you loaded
you can read more in the documentation by Mozilla :https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data

Related

Save file from post response on button click in wordpress

A task:
The user, by clicking on the button, should be able to download and save the file.
The file is creating on another service. It can be obtained by post-request with body and headers.
I am using wordpress and my plugin. I can call a php function using form or jquery.
//Accept: application/pdf
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
$res = curl_exec($ch);
This returns the headers and response body as a string.
(My asp .Net 6 external service returns FileStreamResult from method)
I don't know how to implement saving a file on the user's PC.
As far as I understand, I have two ways:
Download the file using curl on the server side. Then somehow transfer the ready file to the user for saving.
But then there will be an extra load on the backend.
I can create request body and headers on backend. Then execute this post request on the client side.
I am weak in web development...
I think the right way is to make a function on the backend that will return a json with the request body and headers.
I can create a separate php page (I think it's redundant) that will call this function. Or I can call this function from javascript or jquery, but I don't know how to initialize post request to save the file.
Maybe someone has already implemented this behavior and can tell me?
Thanks.
upd. #1
I found this solution:
var request = new XMLHttpRequest();
request.responseType = "blob";
request.open("POST", url, true);
request.setRequestHeader("Content-Type", "application/json");
request.onload = function() {
var url = window.URL.createObjectURL(this.response);
var a = document.createElement("a");
document.body.appendChild(a);
a.href = url;
a.download = this.response.name || "filename"
a.click();
}
request.send(json);
But it looks like a dirty hack.
And it does not work as it should (the file is first downloaded before saving).
upd. #2
I'm willing to change the method from POST to GET, but I still need to pass the ApiKey in the header.
I think my issue is discussed here: https://github.com/whatwg/html/issues/7810
So far, I have not found a way that implements both points:
Adding custom headers to the request.
Showing the file save dialog before it is downloaded.

Offer file download

I am currently working on an AngularJS project with a server backend written in PHP. The frontend and backend communicate entirely in JSON, however, there is an export scenario where the server's output is not JSON encoded but instead a (text or binary) file.
The web application cannot just redirect the client's browser to a download URL as the server requires custom headers in the HTTP request (i.e. an API key) to serve the file. Therefore, I am using $http in AngularJS to initiate an AJAX request. Here is what happens:
File generation on the server side (using PHP with Slim framework):
$export = $this->model->export_cards($project_key);
$this->app->response()->status(200);
$this->app->response()->header("Content-Type", "text/plain");
$this->app->response()->header("Content-Disposition", "attachment; filename=\"export.txt\"");
$this->app->response()->header("Last-Modified", date("r");
$this->app->response()->header("Cache-Control", "cache, must-revalidate");
$this->app->response()->body($export);
$this->app->stop();
This is what happens on the client side (so far):
$http({
method: "get",
url: "/server/projects/cards/export_cards/" + $scope.key,
headers: {
"X-API-Key": session_service.get("api_key")
}
}).then(
function(response)
{
// Success, data received
var data = response.data; // This variable contains the file contents (might be plain text, or even binary)
// How do I get the browser to offer a file download dialog here?
},
function(response)
{
// Error handling
}
);
I successfully receive the file contents in the AngularJS frontend and store them in a variable data. How do I get the browser to display a file download dialog?
The solution must work in Internet Explorer 10+ and reasonably recent versions of Firefox, Chrome and Safari (only desktop versions).
What is the best way to achieve this?
Thank you for your help and let me know if I need to provide any additional information.
Peter
I'm not sure this is possible.
Could you either:
Supply the API key directly, eg:
location.href = "/server/projects/cards/export_cards/" + $scope.key + '?api_key=' + session_service.get("api_key");
Or, have your API return a temporary, time-expiring URL for the file download, and then use location.href to access this URL.

Dropbox api - php - display image file in website

This might sound trivial for some of you, but I need to be sure...
I simply need to use dropbox for 2 things :
upload image files via php from my web server with the possibility of creating folder (as I would do on a normal web server) or sync folders from my web server to dropbox via rsync;
display these image files in a web page
I have downloaded the api sdk, then run into the 64bit exception error, then invalid redirect-uri...
So I would really appreciate if someone can reply to my 2 questions above and point me to a good example to just do that.
I solved it in another way. Rather than displaying the raw-file I use the API to generate Direct Download Link. This will give me a weblink that I then modify by adding a "raw=1" and subsitute the "dl=0" with "dl=1". This new link than then be used as the source for a normal html image.
As per above suggestions
import dropbox
import json
import httplib, urllib, base64
access_token='your token'
client = dropbox.client.DropboxClient(access_token)
url = client.share('test.jpg',short_url=False)
imageurl = url['url'].replace("dl=0","raw=1")
body = {
"url":imageurl
}
print json.dumps(body)
headers = {
# Request headers
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': '00759a20e705487a91e4db51b80bdfa7',
}
params = urllib.urlencode({
})
try:
conn = httplib.HTTPSConnection('api.projectoxford.ai')
conn.request("POST", "/emotion/v1.0/recognize?%s" % params,json.dumps(body), headers)
response = conn.getresponse()
data = response.read()
print(data)
conn.close()
except Exception as e:
print("[Errno {0}] {1}".format(e.errno, e.strerror))

Problems doing ajax-requests with a Phonegap application

I'm trying to create a simple RSS reader with Phonegap and jQuery.
I am following this tutorial: http://visualrinse.com/2008/09/24/how-to-build-a-simple-rss-reader-with-jquery/.
I've managed to get this working just fine when I try out the code in my browser. The php-file fetches the feed and outputs it just like I expect it to. But when I run the same file from within my compiled Phonegap application the ajax-request just returns the contents of the php-file (the php-code, not the executed result).
I've spent hours Googling this and tried numerous tutorials and tweaks. I found no solutions in the offical Phonegap forums either. What am I doing wrong? The problem seems to be PHP not responding to the request. I've tried to move the php-file to a different domain but the result is the same, it works in my browser but not in the compiled app.
Here's the jQuery code that initiates the ajax-code:
function get_rss_feed() {
//clear the content in the div for the next feed.
$("#feed_content").empty().html('<img class="loader" src="js/images/ajax-loader.gif" alt=""/>');
$.ajax({
url: 'http://192.168.1.7/rssApp/www/rss-proxy.php?url=http://www.nytimes.com/services/xml/rss/nyt/GlobalHome.xml',
success: function parseRSS(d) {
//find each 'item' in the file and parse it
$(d).find('item').each(function() {
//name the current found item this for this particular loop run
var $item = $(this);
// grab the post title
var title = $item.find('title').text();
// grab the post's URL
var link = $item.find('link').text();
// next, the description
var description = $item.find('description').text();
//don't forget the pubdate
var pubDate = $item.find('pubDate').text();
// now create a var 'html' to store the markup we're using to output the feed to the browser window
var html = "<div class=\"entry\"><h2 class=\"postTitle\">" + title + "<\/h2>";
html += "<em class=\"date\">" + pubDate + "</em>";
html += "<p class=\"description\">" + description + "</p>";
html += "<a href=\"" + link + "\" target=\"_blank\">Read More >><\/a><\/div>";
//put that feed content on the screen!
$('#feed_content').append($(html));
});
$('#feed_content img.loader').fadeOut();
}
});
};
Here's the rss-proxy.php that loads the XML from the url and outputs it:
<?php
// PHP Proxy
// Loads a XML from any location. Used with Flash/Flex apps to bypass security restrictions
// Author: Paulo Fierro
// January 29, 2006
// usage: proxy.php?url=http://mysite.com/myxml.xml
$session = curl_init($_GET['url']); // Open the Curl session
curl_setopt($session, CURLOPT_HEADER, false); // Don't return HTTP headers
curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Do return the contents of the call
$xml = curl_exec($session); // Make the call
header("Content-Type: text/xml"); // Set the content type appropriately
echo $xml; // Spit out the xml
curl_close($session); // And close the session
?>
I've finally managed to solve this!
It turns out that you need to whitelist the server you wish to request from your PhoneGap application in Xcode if you want to do requests to a certain domain (be it your localhost or whatever).
The reason that I didn't found this out earlier was that I didn't check for errors in the ajax response. Once I did that I got the http status code 401 (Unauthorized) and error message "Whitelist rejected".
To fix this I opened the file PhoneGap.plist in my project and under the key ExternalHosts i added a new item with the value: *.localhost.
I also changed the ajax url to:
url: 'http://localhost/rssApp/www/rss-proxy.php?url=http://www.nytimes.com/services/xml/rss/nyt/GlobalHome.xml'
I compiled and run the application on the iOS Simulator and my localhost server responded with a perfectly successful ajax response!
For every external host that you wish your application to connect to you must add it to the list of ExternalHosts. For example if you wish to access an API on http://google.com/maps/api.php you must add *.google.com to your list.
Kind of annoying when you try to figure out why the server isn't responding, but I guess it's good for security reasons. Hope this helps someone else out there who's struggling with simple ajax requests from their PhoneGap application!
It looks like you are running your server locally (based on the 192.168.x.x IP address) which means that only devices connnected to your network can access it. You could connect the phone to the same wifi network as your computer as a temporary fix. But you're going to need to host this on a real server for it to be accessible over the internet.
You could also forward port 80 on your router to this IP address and then use your actual IP address (see whatsmyip.org) in your request url. But that's not a really stable solution.

Asynchronous POST request from a Firefox's web worker

I am trying to make an asynchronous request with POST method from a web worker used in my extension. The thing is that it does not work for me.
On the server side I have PHP script listening for data in $_POST variable. Although I am able to establish connection to the server, and even pass some data in URL (GET), the $_POST is always empty.
Here is the latest code I'm using in the web worker:
var serviceUrl = "http://localhost/pfm/service/index.php";
var invocation = new XMLHttpRequest();
if(invocation){
invocation.open('POST', serviceUrl, true);
invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
invocation.setRequestHeader('Content-Type', 'text/plain');
invocation.onreadystatechange = function(){processResponse(invocation);};
invocation.send("action=init");
}
(borrowed from MDN web site when I got an idea that the issue was the same origin policy)
Prior to this I was using a rather obvious and ridiculously simple:
var serviceUrl = "http://localhost/pfm/service/index.php";
var xhr = new XMLHttpRequest();
xhr.open("POST", serviceUrl, true);
xhr.onreadystatechange = function(){processResponse(receiptStoreRequest);};
xhr.send("action=init");
In this case the request also passed, but the $_POST was still empty.
Is it possible that POST-requests are not allowed on web workers?
Right now everything is being tested on localhost.
Don't set the content type text/plain but
invocation.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
In case you're developing for Firefox 4+ you might also be interested in FormData objects

Categories