power computing

Because I am in love with ExtJS  I tend to write up backends for content in Dancer and ExtJS. When designing a CMS for a client invariably there is some sort of attachment that needs to go along with the content. People love PDF, Word, Excel files etc and they nearly always need to be able to “download” these attachments from the CMS.  We can’t do it with AJAX requests so having a call to Ext.Ajax(…) behind a button or contained in some event wont work. In the past I would simply use a component written by a very smart fellow who goes by the name of condor, but with ExtJS4 this component needed updating.

Ext.define('CMS.view.FileDownload', {
    extend: 'Ext.Component',
    alias: 'widget.FileDownloader',
    autoEl: {
        tag: 'iframe', 
        cls: 'x-hidden', 
        src: Ext.SSL_SECURE_URL
    load: function(config){
        var e = this.getEl();
        e.dom.src = config.url + 
            (config.params ? '?' + Ext.urlEncode(config.params) : '');
        e.dom.onload = function() {
            if(e.dom.contentDocument.body.childNodes[0].wholeText == '404') {
                    title: 'Attachment missing',
                    msg: 'The document you are after can not be found on the server.',
                    buttons: Ext.Msg.OK,
                    icon: Ext.MessageBox.ERROR   

I have created the FileDownload component as a view because frankly it did not seem like it should be a controller or model. If the attachment is not found simply return a 404 with a body of 404 and you can alert the end user. For Dancer I have something like the following

get '/:id/download' => sub {
    my $attachment = fetch_atatchment_from_database(params->{ id });
    if($attachment) {
        my $file = '/attachments/' . $attachment->{ id } . '/' . $attachment->{ file };
	    if(-f public_directory . $file) {
	        header 'Content-Disposition' => 'attachment; filename="' . $attachment->{ file } . '"';
    		return send_file($file);
	send_error("404", 404);
	return "404";

In order to use the component save the code into App.view.FileDownload.js, require it in your app.js

requires: [

Then put it somewhere appropriate in your viewport or layout or page etc. I generally place it in the south region of my apps viewport.

    region: 'south',
    html: 'South',
    items: [
            xtype: 'FileDownloader',
            id: 'FileDownloader'

Then in order to use it simply grab a reference to the FileDownloader and load that sucker up

var downloader = Ext.getCmp('FileDownloader')
    url: '/attachments/' + record.get('id') + '/' + record.get('file')

It should be noted that it is ver important that your send the header Content-Disposition: attachment; filename='...' otherwise you’ll be pulling out your hair as to why the document didn’t download.

Thanks go to


The Sencha Team