Persevere includes a special class/table called
File designed to hold various binary and text files. Each
File instance can be referenced like any other object in Persevere. There are several options for creating new
File instances and sending binary data to Persevere for storage. Persevere supports several very RESTful mechanisms that follow the protocol of the HTTP specification (RFC 2616). This includes directly POSTing a binary file to the File table, using PUT to add a file as a property of an existing JSON object, and using PUT to add an alternate representation of a JSON object in Persevere. Persevere then properly handles HTTP-specified content negotiation to deliver the correct representation of an object, whether it be application/json or a binary MIME type.
To understand how to use this capability, we will look at an example application. We will create a button that allows us to upload a new image for a product object.
<form action="/Product/1" method="POST" enctype="multipart/form-data"> <input type="file" name="picture" size="40" /> <input type="submit" value="Upload Image" /> </form>
When a user selects a file and submits it with this form, the file will be uploaded to Persevere, and the file will be set as the value of the
picture property of the Product with an id of
1. Consequently, once the file is uploaded we can access the file with the URL of
/Product/1.picture. If the user uploaded an image, we can now show that image in a page with the following HTML:
<img src="/Product/1.picture" />
Of course if you are using Persevere, you probably won’t be using static object id references in your HTML. Naturally, you can set the target URL (
myForm.action = productId;
With the HTML for the file upload form defined above, when the user submits the form, Persevere will simply return a blank page. This is probably not desirable. This can easily be handled by creating an invisible iframe as the target of the page. We can then add an
onload for the iframe so we can be notified when the file is uploaded:
<form target="uploadTarget" action="/Product/1" method="POST" enctype="multipart/form-data"> <input type="file" name="picture" size="40" /> <input type="submit" value="Upload Image" /> </form> <iframe style="display:none" name="uploadTarget" onload='onFileUploaded()'></iframe>
You can then define a function
onFileUploaded that will be called when the file is finished uploading. Now, when a user chooses a file and submits it, the
onFileUploaded will be called so the application can carry on the next step after uploading.
Persevere includes robust secure protection against cross-site request forgery (CSRF). Because form submissions can be triggered from any site to one of your server’s URLs, Persevere will only process the request with the currently logged in user’s authorization level if it is certain that the user authorized the request. Persevere can verify the request if the
Referer header has a URL that corresponds to the
Host header. However, not all users’ requests will have a
Referer as some proxies strip this header. In order to ensure that the form submission works for all users, the request must be verifiable by including a client_id query parameter that matches a predefined client id for the connection to Persevere with the
Client-Id header. This client id can be established by generated a random client id number and sending it to the server with an XHR request:
clientId= Math.random(); var xhr = new XMLHttpRequest(); xhr.open("GET","/Product/",true); xhr.setRequestHeader("Client-Id", clientId); xhr.send(null);
If you are using Dojo on the front-end, it should automatically be sending the
Client-Id header on all requests. Therefore the client id can be accessed from the property
clientId = dojox.rpc.Client.clientId;
Now, to use this client id to verify the form submission, we can add it to the target URL:
myForm.action = productId + "?client_id=" + clientId;
This will ensure that the request is properly authorized for all users, while allowing Persevere to properly protect against any CSRF attacks on your site.
An alternate demonstration of this mechanism can be seen in the friends example in Persevere’s
examples directory. In this example application, you can see a simple front-end for a database of friends. The front-end includes a form for adding an image for each of your friends, and this image is displayed in the listing of friends.
Getting More Information about a Binary Resource
In addition to using a binary file’s URL to show images, you can also interact with the file. The file object includes
content properties. The
contentType property holds the MIME type of the object. The
content is a binary object that holds the byte data for the object. If an image has been uploaded for the Product instance with an id of
1, from the server you could run:
var file = load("Product/1.picture"); file.contentType -> "image/jpeg"
Storing Binary Data in Persevere
Persevere includes several mechanisms for storing and retrieving data. These mechanisms include PUT and POSTs based on the HTTP specification for creating and updating resources, and allows for defining alternate MIME types of existing resources. In addition, Persevere also includes special support for browser-based form submission of binary data to facilitate Ajax-style applications that wish to provide a means for uploading data from the browser to the server.