Fine Uploader 5.0 – Concurrent Chunking

Update: July 1, 2014 – 5.0.3 Hotfix Release

  • Fixes an issue where non-image files are not uploading when scaling is turned on and sendOriginal is off. (#1238)

Update: June 17, 2014 – 5.0.2 Hotfix Release

  • Fixes an issue with iOS subsampling images larger than 2 megapixels during the image scaling process (#1231)

Update: June 5, 2014 – 5.0.1 Hotfix Release

  • On traditional endpoints, when a chunked upload completes the server responseJSON does not get passed to the handler (#1227)

We are excited to announce the official release of Fine Uploader version 5.0.

This is a major version release due to some breaking changes we made to support a great new feature: Concurrent Chunking! This is a feature that other libraries have attempted to implement, without success. Fine Uploader is the ONLY upload library to offer concurrent chunking. This new feature is targeted at applications that expect users to upload single very large files.

Our internal tests that involved uploading multiple chunks for a specific file concurrently showed significant improvements in bandwidth utilization. For example, on our internal network, sending a 110 MB file to S3 with chunk sizes of 5 MB took about 22 seconds when chunks were uploaded one-at-a-time (with concurrent chunking disabled). When maxing out the default maxConnections for that file (3 chunks at once, concurrent chunking enabled) the same file uploaded in about 12 seconds. All other users who tested this feature during the beta phase reported similar results.

You can read more about this new feature on the associated feature page on the docs site.

Other Features:

Support for other promise implementations

This change allows you to use any A+ certified promise impl (such as RSVP or Q) when communicating with Fine Uploader. You may also continue to use Fine Uploader’s own (non-A+-certified) promise implementation.

Dynamically Set Custom Headers

This is targeted specifically at traditional upload endpoints (not S3 or Azure). A new method, setCustomHeaders, has been added to allow you to set upload request headers at any time after the upload instance has been created.

Bug Fixes:

  • Form support parser ignores textarea elements (#1204)
  • Retry Button should remove qq-hide while visible (#1193)
  • File size of 20 bytes is reported for all files in older browsers (#1161)
  • Google Closure Compiler Error (#1163)
  • dataTransfer.items is an object in Chrome and passes the truthy test then throws on the array indexer after && causing a bug (#1166)
  • Total Progress Bar Shows/Hides in rapid succession (#1175)

Next…

As always, for up-to-date information about features and fixes planned for the next release, please see the milestone in the Github issue tracker. At the writing of this post, we are in the process of planning the 5.1 release.

As always, we have continued to add the features that you all demand the most. Thanks again for your continued support. It is due to the backing of commercial license holders, everyone reporting bugs, those who suggest great features, and all the people that have expressed their support for this library that make it as great as it is.

Thanks for being a part of Fine Uploader!

Fine Uploader 4.4 Released!

We are excited to announce the official release of Fine Uploader version 4.4. That’s the 37th release! As always, we have continued to add the features that you all demand the most. Thanks again for your continued support. It is due to the backing of commercial license holders, everyone reporting bugs, those who suggest great features, and all the people that have expressed their support for this library that make it as great as it is.

Here are some notable additions that are part of the 4.4 release:

Features:

Client-side image scaling

This feature gives Fine Uploader the ability to resize images on the client, and upload the resized versions (along the with the original and all EXIF data) to your server. That means less work for your server and saved money for those who are paying by the clock cycle.

The documentation on image scaling contains information on how to set up scaling on the client, and modify the various options.

Total Progress Reporting

A brand new feature for users of UI mode is total progress reporting. Fine Uploader has had progress-bars for each file for a while now, but one feature that was requested was to show progress for all of the uploads. It has always been possible to hook into existing events to display an aggregate progress-bar, but with this latest release it has become much easier. See the documentation on progress bars for more information.

Also part of this feature is the addition of the totalProgress event. This event will report the total progress for a batch of files in the AJAX uploader. The event will be emitted when an individual file’s progress changes, a file is added, and when a file is cancelled.

Folder Paths

It is now possible to detect the full paths to any files or directories added to the uploader as children of a directory when dragging and dropping a folder in Google Chrome and Opera.

Multiple-endpoint Custom Builds

The custom build generator now has the capability to create builds with multiple endpoints. This means you should never need to create more than one custom build because you can select any combination of one or more endpoints.

Bug Fixes:

  • Allow function parameters in S3 modes (#1105)
  • Drop zones are properly hidden when dragging in and out of browser in Safari, IE10, and IE11 (#1034)
  • Blobs and Data URI generation in Androids’ stock browser (#1146)
  • setName now updates the name UI (#1138)
  • drawThumbnail correctly renders image in iOS (#1131)
  • Prevent resumption of currently uploading file (#1101)
  • Thumbnail process correctly handles unreadable files#1143
  • Fix exception when using standalone drag and drop module for the S3 uploader (#1132)

Next…

As always, for up-to-date information about features and fixes planned for the next release, please see the milestone in the Github issue tracker.

The next release is already in the works and some big things are going to be included. We plan to take a serious look into implementing a few new ideas to the basic upload algorithm with the goal of supporting multiple simultaneous chunked uploads.

Thanks for being a part of Fine Uploader!

Fine Uploader 4.3: Support for HTML Forms and Windows Azure

Update: February 11, 2014 – 4.3.1 Hotfix Release

This hotfix release addresses the following issues:

  • HTTP is now used as the default scheme for S3 endpoints rather than HTTPS (#1129)(Reverts: #1119)
  • Apply display.fileSizeOnSubmit only to files submitted by the user (#1130)

Here are some notable additions that are part of the 4.3 release:

Upload Files Directly to Azure From Your Browser

In 3.8, we added the ability to upload directly to Amazon’s S3 service directly from your browser, in all supported browsers. Starting with 4.3, you will be able to send these files directly to Microsoft’s Windows Azure Blob Storage service as well. You can read more about this feature on our documentation site. We have even provided a C# example for signing the requests that Fine Uploader Azure makes.

Please note that uploads directly to Azure are not possible in Internet Explorer 9 and older. This is due to a shortcoming of Azure’s API.

Easily Connect Fine Uploader to an Existing HTML Form

You can now easily connect Fine Uploader to an existing HTML form. Fine Uploader will send all of the form fields along with each upload request. It will even validate the form for you, if the browser supports this behavior. If you follow all of the (overridable) conventions in place, you can achieve this with only one total line of JavaScript! You can read more about this feature on our documentation site.

Other enhancements:

Bugs fixed:

Next…

As always, for up-to-date information about features and fixes planned for the next release (4.4), please see the milestone in the Github issue tracker. We will begin to plan the 4.4 release shortly after this release of 4.3.

Uploads without any server code

If you just want to simply accept files from users, why should you have to write server-side code? Well, if you are using Fine Uploader S3 version 4.2 or later, you don’t have to worry about server-side languages anymore! This blog post accompanies a live demo of this workflow at https://fineuploader-s3-client-demo.s3.amazonaws.com/index.html.

Summary

Starting with Fine Uploader S3 4.2, the “no server” upload workflow is fully supported. This means that you only need to host your JavaScript and HTML files. Fine Uploader S3, AWS, and your identity providers take care of the heavy-lifting for you.

The workflow is simple:

  1. authenticate your users with the help of an identity provider, such as Google
  2. Use the temporary token from your ID provider to grab temporary access keys from AWS
  3. Pass the keys on to Fine Uploader S3
  4. Your users can now upload to your S3 bucket

Requirements

A client-side workflow such as this one means that you must ensure your users are utilizing a modern browser (not IE9 or older). Some of the SDKs used here (mostly the AWS JavaScript SDK) will not work on older browsers.

Other things you will need to make this happen:

  • Fine Uploader S3 4.2+
  • OAuth/login JavaScript SDK from Google, Facebook, or Amazon
  • A simple web server to host your static content, such as a public-read S3 bucket
  • An AWS account
  • AWS JavaScript SDK

Concepts

You should be familiar, at a high-level, with OAuth 2.0, which is the standard used by Google, Amazon, Facebook, and other similar identity providers. An identity provider here will allow you to request temporary credentials from AWS on behalf of the authenticated user. These credentials are required by Fine Uploader S3 to upload files to your S3 bucket without any server-side intervention on your part. It is important to ensure traffic between your web app and the identity provider be secured via SSL (HTTPS) when using an OAuth 2.0 ID provider.

You must also be familiar with:

  • HTML
  • JavaScript
  • Amazon’s Simple Storage Service (S3)
  • Fine Uploader S3

Setting up your identity providers

The live demo of this workflow allows you to choose between Google, Facebook, and Amazon as identity providers. You will need to register your application with each of these identity providers, record the assigned client ID, and specify the domain(s) of your web application in order to ensure that authentication against your registered application can only occur on your application’s domain(s).

Google

1.) Login to the Google Cloud Console.

2.) Create a new project.
empty google cloud console project list

google cloud console create project

3.) Enable the “Google+ API”
enable Google+ API

4.) Create a new OAuth Client ID in the “Credentials” section of the “APIs & auth” side menu. The application type will be “Web application”. You should include the domain(s) of your web app in the “Authorized Javascript origins” text box. You can leave the “Authorized redirect URI” field blank.
create OAuth client ID

5.) Record the “Client ID for web application” value.
OAuth client ID

Facebook

1.) Visit the Facebook developers page.

2.) Invoke the “Apps” menu, and click “Create a new app”. Fill in the fields as appropriate.
create facebook app

3.) Record the App ID after creating the app.
facebook app ID

4.) Click on the “Settings” option on the right, and then click the “Add Platform” bar.
facebook app add platform

5.) Click “Website”.
facebook app choose app type

6.) Fill out the “Site URL” under the new “Website” section. This should be the domain of your web app. Finally, save your changes.
facebook app details

Amazon

1.) Sign in to Amazon’s App Console. with your AWS account. Click on the “Register new application” button.

2.) Fill out the relevant form fields. Note that all fields other than the logo URL are required.
register AWS app

3.) Record the App ID.
AWS app ID

4.) Expand the “Web Settings” section, and add your app’s domain name(s) in the “Allowed JavaScript Origins” section. Note that this URL must be SSL/HTTPS.
AWS web app settings

Creating and securing your S3 bucket

If you haven’t done so already, you will need to create an S3 bucket to receive your user’s files. This is covered in some detail in the “Configuring your buckets” section of the initial Fine Uploader S3 blog post.

In addition to the AllowedHeader values mentioned in the CORS section of the bucket configuration documentation, you will also need to ensure that the x-amz-security-token header is allowed.

Setting up your IAM roles

You will need to create a separate IAM role for each identity provider. Each role will link an identity provider to specific client-side permissions needed by Fine Uploader S3 to send files to your S3 bucket.

1.) Navigate to the IAM roles section of the AWS console.

2.) Click the “Create New Role” button & fill in a name.
IAM role name

3.) Choose the “Role for Identity Provider Access” option on the next step. Click the “Select” button next to “Grant access to web identity providers”.
IAM role for ID provider

4.) Pick an identity provider. If you are using Facebook or Amazon, paste the client ID you recorded when you registered your app in the “Application Id” field. If you are using Google, paste the application ID you recorded in the “Audience” field instead. Click “Continue”. You will be brought to a “Verify Role Trust” step. Click “Continue” again.
AWS role ID provider
AWS role verify ID provider

5.) Expand the “Policy Generator” section, and click “Select”.
AWS role policy generator

6.) You must now specify permissions for your S3 bucket. Fine Uploader S3 only needs the PutObject permission to upload files. However, if you intend to make all files publically viewable (as we do in the live demo), you will also need to include the PutObjectAcl permission. The Amazon Resource Name includes the name of the S3 bucket that will receive files, in the format “arn:aws:s3:::YOUR_BUCKET_NAME/*”. After you have filled all of this out, click “Add Statement”, then “Continue”.
IAM role S3 permissions
IAM role bucket name

7.) Click “Continue” on the “Set Permissions” page step that appears next, then “Create Role” on the final step.
IAM role set permissions

8.) Click on your new role in the roles list, then the “Summary” tab on the bottom pane, and record the Role ARN ID.
IAM role ARN

The code

There is a live demo with accompanying commented code as well. Note that the live demo is hosted in a public S3 bucket. This is more apparent when looking at the URL.

The live demo contains the following files:

index.html

Entry point for the live demo. It pulls in all other client-side files and resources. Also, it contains HTML for the Google, Facebook, and Amazon login buttons, as well as other elements required by the third-party JavaScript SDKs. A customized version of a Fine Uploader UI template is also present in the head element. Finally, there is some code to ensure the demo is only displayed if a modern browser is in use. We make use of conditional comments here to display & load the demo if a modern browser is in use, or a message explaining why the demo is not functional if IE9 or older is present.

3 JavaScript files to make use of the identity provider SDKs

We have one JavaScript file for each identity provider. These are used to track authentication requests and pass the token received from a successful auth request on to the AWS SDK. Also, they are used to notify the user when the bearer token has expired (asking them to re-authenticate by clicking on the login button). The files are amazon-auth.js, google-auth.js, and facebook-auth.js.

You must ensure the Role ARN and app IDs are filling in appropriately in these files. Note that the app ID for the Google ID provider is attached to the login button element in index.html as a data attribute. Also note that the facebook and aws files include a providerId in the call they make to the assumeRoleWithWebIdentity method. Google’s ID provider does not have such a property though.

aws-sdk-glue.js

Used to obtain temporary credentials (keys) supplied to Fine Uploader. The bearer tokens obtained from the identity providers are used to generate these credentials.

Fine Uploader S3 built source files & resources

We also must include the Fine Uploader UI S3 jQuery JavaScript, CSS, and other resource files (images/placeholders). You can generate your copy at http://fineuploader.com/customize.

custom.css

Some CSS to enhance the style of this demo.

fineuploader-glue.js

Creates an instance of Fine Uploader UI S3 jQuery. The “complete” and “credentialsExpired” events are observed. The former includes a button that links to the uploaded file in S3 next to the file item in the UI after a successful upload. The latter asks the AWS credentials code for new credentials before they expire.

Additional reading

Fine Uploader 4.2 Released!

Update: January 23, 2014 – 4.2.2 Hotfix Release

This hotfix release addresses the following issues:

  • None of the submitted files have a QUEUED status after uploading has started (#1104)

Update: January 16, 2014 – 4.2.1 Hotfix Release

This hotfix release addresses the following issues:

  • Drag and Drop Module: Exception when iterating over FileList in Safari. (#1100)

After a bit of a holiday hiatus, the Fine Uploader team is excited to release to you our latest version. It’s packed with features gathered from your suggestions and bug reports. Here are some notable additions that are part of the 4.2 release:

Features:

Serverless S3 Uploads

Fine Uploader 4.2 brings the ability to upload directly to Amazon’s S3 without the need for an intermediary signature server. That means you can enable file uploads for your users with just the a little bit of HTML and Javascript. Ray has developed a fully featured example, and an accompanying tutorial blog post that you should look at to get a taste of what is possible with this new feature. For more information on uploading to Amazon S3 with just Fine Uploader, see the documentation.

Get a Drop Zone From a File ID with the getDropTarget() API Method

This new UI only API method allows you to discover which drop zone element a specific file is associated with. Along with an API method, the processingDroppedFilesComplete event has an appended dropTarget parameter. As always, the documentation for the method and the drag and drop module have more details.

Get a File ID From an HTMLElement with the getId() API Method

Another new UI only API method — getId — has been added to aid in retrieving a specific file’s id when you know where it is in the DOM.

Support for Nested Dropzones

The allowance of nested drop zones supports workflows much like Dropbox UI where the entire container may be a drop zone, and rows within the container may be drop zones. The drag and drop documentation has more details to get you started.

Bug Fixes:

Other Improvements:

Next…

As always, for up-to-date information about features and fixes planned for the next release (4.3), please see the milestone in the Github issue tracker. We will begin to plan the 4.3 release shortly after this release of 4.2.

Thanks for being a part of Fine Uploader!

Fine Uploader 4.1: Pause Uploads, Image Validation, & Support for More Browsers

Update: December 27, 2013 – 4.1.1 Hotfix Release

This hotfix release addresses the following issues:

  • Fine Uploader S3: Uploads fail to complete in Safari 5. (#1080)

Here are some notable additions that are part of the 4.1 release:

Validate Image Dimensions

If you have a need to restrict uploaded image dimensions in your application, you can now enforce this client-side in all browsers other than IE9 and older, Safari 5.1 and older, and Android 2.3.x and older. You can restrict the height and/or width of submitted images via a new validation option property. Please see the validation feature documentation for more details.

Pause Uploads

This feature mainly compliments the chunking and auto-resume features. For example, while it is not necessary to “pause” an in progress upload to resume it later, it is probably much more intuitive for end users to do so. This feature also provides the ability to upload queued items immediately (or sooner) simply by pausing in-progress uploads that are lower priority.

See the pause feature documentation for more details.

Internet Explorer 11 Support

IE11 is now fully supported. In fact, all features available in IE10 are now available for
IE11 users.

Opera Support

We have finally added support for Opera. Note that this only includes Opera 15 and newer. As of Opera 15, Chromium is used “under the hood”. As a result, all features supported in Chrome are also now supported when you are using Opera!

Mobile Chrome Support

We now support the Chrome browser on iOS6+ and Android 4+. In iOS, Chrome supports all of the same features as mobile Safari, except for progress reporting. On Android, mobile Chrome supports all of the features supported by the Android stock browser as well as progress reporting.

Other fixes & changes

Next…

As always, for up-to-date information about features and fixes planned for the next release (4.2), please see the milestone in the Github issue tracker. We will begin to plan the 4.2 release shortly after this release of 4.1. Note that the release cycle for 4.2 will likely be longer than normal due to the upcoming holidays.

Full-stack JavaScript Image Uploader using AngularJS & node.js

Update: March 10, 2014 – 4.4.0 Release

The total progress code from earlier versions of this demo has been replaced by the total progress feature implemented in Fine Uploader 4.4.

AngularJS is becoming a very popular client-side web application framework. As you may know, Fine Uploader is also quite a powerful cross-browser javascript file uploader. So, why not use both? Why not indeed. This post will walk you through writing an AngularJS directive for Fine Uploader. Bootstrap and jQuery will also be used for styling and event handling/DOM manipulation (respectively). Complete code for client and server side is also provided that includes a node.js backend.

Demonstrated Features

The following features will be demonstrated/enabled in the example code:

Dependencies

By default, Fine Uploader has no dependencies, not even jQuery. However, this example makes use of a few third-party libraries in order to make your life easier. Why re-invent the wheel anyway?

Client-side

Server-side

Getting Started

This example assumes you are already comfortable with AngularJS, node.js, Bootstrap, JavaScript, and jQuery. If you are not familiar with any of these technologies, you should spend some time browsing the respective documentation first.

In order to implement this example in your web application, you must follow these steps:

  1. Download Fine Uploader 4.4.0+ and add it to your server
  2. Install node.js on your server
  3. Download the angularjs-nodejs example code from the fine-uploader-examples GitHub repo. Add the files to your server.
  4. Run npm install in the directory housing the example code you downloaded. This will pull in all server-side dependencies needed by the node server.
  5. Start up your server, and begin uploading!

Notable Custom Features Specific to this Example

If you want to play around with all of the features provided by Fine Uploader UI in this example, run the example code in your own environment. Here, we will point out some of the notable custom features specific to our image uploader example.

Dynamic Upload Button

The upload button is not only styled with the help of Bootstrap, we also set the text in our Angular directive based on the capabilities of the current browser. For example, if the current browser supports multiple file selection, we want to reflect that in the text of the upload button:
upload files button

What if the browser does not support multiple file selection (IE9 and older)? Well, our button should look something like this:
select a file

Here’s the relevant portion of our Fine Uploader UI template:

<div class="qq-upload-button-selector upload-btn btn btn-lg btn-primary">
    <div ng-bind="uploadButtonText"></div>
</div>

…and here’s the related JavaScript in our AngularJS directive, which is called in our directive’s link function:

function initButtonText($scope) {
    var input = document.createElement("input");

    input.setAttribute("multiple", "true");

    if (input.multiple === true && !qq.android()) {
        $scope.uploadButtonText = "Select Files";
    }
    else {
        $scope.uploadButtonText = "Select a File";
    }
}

As you can see, we are determining if the file input element supports the multiple attribute before we set the upload button text. We have bound the upload button text to the uploadButtonText property of the current scope object, so we can update it easily inside our directive.

Dynamic & Scrollable Drag & Drop Zone with File List

Similar to the upload button, it is potentially useful to include a message in the drop zone that provides useful startup instructions to your users. When they first look at the page, it may be confusing for some, as far as options are concerned. The best approach here is to re-style the drop area in Fine Uploader UI’s default template, making the drop zone visible, scrollable, fixed-height, and including background text with specific information for users on how to proceed.

There are three possible states for the background text for the drop zone. These states correspond to the capabilities of the current browser. Chrome provides the ability to drop folders AND files. Everything other than IE9 and older (and mobile browsers) provide the ability to drop files. Drag & drop does not apply, or is not possible, in mobile browsers along with IE9 and older.

Here’s what the drop zone will look like in Chrome:
drop files or folders

…and Firefox:
drop files

… and IE9 (or older):
select a file drop

Here’s the relevant portion of our Fine Uploader UI template, along with some custom CSS overrides:

<div class="qq-upload-drop-area-selector uploader-drop-zone">
    <span class="drop-zone-text" ng-bind="dropZoneText"></span>
    <ul class="qq-upload-list-selector qq-upload-list">
        <li class="file-container">
            <!-- file list template items go here... -->
        </li>
    </ul>
</div>
.uploader-drop-zone
{
    /* Static height drop zone that also contains the file list, with a border */
    height: 300px;
    border: dashed 1px lightgrey;
    -webkit-border-radius: 8px;
    -moz-border-radius: 8px;
    border-radius: 8px;
    text-align: center;

    /* Make the drop zone scollable */
    overflow-y: scroll;
}

.drop-zone-text
{
    /* Vertically & horizontally center the dynamically generated drop zone message
    inside the drop zone & make it a bit presentable */
    position: absolute;
    vertical-align: middle;
    line-height: 300px;
    font-size: 200%;
    width: 100%;
    left: 0;
}

…and here’s the related JavaScript in our AngularJS directive, which is called in our directive’s link function:

function isTouchDevice() {
    return "ontouchstart" in window || navigator.msMaxTouchPoints > 0;
}

function initDropZoneText($scope, $interpolate) {
    if (qq.supportedFeatures.folderDrop && !isTouchDevice()) {
        $scope.dropZoneText = "Drop Files or Folders Here";
    }
    else if (qq.supportedFeatures.fileDrop && !isTouchDevice()) {
        $scope.dropZoneText = "Drop Files Here";
    }
    else {
        $scope.dropZoneText = $scope.$eval($interpolate("Press '{{uploadButtonText}}'"));
    }
}

Image Thumbnails/Previews

Fine Uploader UI already provides the (optional) ability to generate client-side pre-upload thumbnails for image files. This is possible in all browsers other than IE9 and older. The previews are rendered next to the file in the file list immediately after it is chosen by the user. But, what if you want a consistent look for your image uploader in all browsers? Well, you can easily return a URL to the uploaded image in your server’s response to the upload request. Fine Uploader UI will then display the scaled thumbnail next to the file.

So, all browsers will look something like this:
Screen Shot 2013-11-01 at 2.23.10 PM
Note that is after uploads have completed, but preview will appear before the upload even starts in modern browsers. Also notice that we have made the file list slightly opaque, so we can still see the drop zone background text.

In order to ensure thumbnails displayed (both scaled and oriented (where supported)), we have included this single line inside the file-info container in our template:

<img class="qq-thumbnail-selector" qq-max-size="50" qq-server-scale>

No javascript is needed for this at all. Fine Uploader UI takes care of all of the client-side work for us if the qq-thumbnail-selector IMG element is present in our template. Since we want thumbnails to appear for IE9 and older after the upload completes, we need to be sure and return a URL for the image in our server response. To be certain that we only do this if the browser does not support client-side preview generation, we send an appropriate parameter along with the upload request, like this:

$(element).fineUploader({
    request: {
        ...
        params: {
            sendThumbnailUrl: !qq.supportedFeatures.imagePreviews
        }
    }
    ....
});

Then, server-side, we include a thumbnailUrl parameter with our response that includes a URL to the uploaded file, but only if the sendThumbnailUrl parameter in the request has a value of true. See the server.js file in the example directory for implementation specifics.

Now, we go a bit further in this example, and allow users to see a larger preview, generated on-demand, simply by clicking on the small thumbnail next to the file. This is possible in all browsers, and looks something like this:
larger preview

For this feature, we rely on Bootstrap’s modal component to house the larger thumbnail, along with Fine Uploader’s drawThumbnail API method to dynamically generate a larger thumbnail.

For this specific feature our HTML document includes this:

<div id="previewDialog" class="modal fade">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
                <h4 class="modal-title" ng-bind="previewTitle"></h4>
            </div>
            <div class="modal-body text-center">
                <div class="progress progress-striped active">
                    <div class="progress-bar" role="progressbar" style="width: 100%;"></div>
                </div>
                <img id="previewContainer">
            </div>
        </div>
    </div>
</div>

We have some javascript in our Angular directive as well:

$(element).fineUploader({
    ...
    callbacks: {
        onSubmitted: function(id, name) {
            var $file = $(this.getItemByFileId(id)),
                $thumbnail = $file.find(".qq-thumbnail-selector");

            $thumbnail.click(function() {
                openLargerPreview($scope, $(element), largePreviewSize, id, name);
            });
        },
        ...
    }
});

The above javascript is part of the code that creates the Fine Uploader UI instance inside our Angular directive’s link function. It attaches a click handler to the thumbnail element for each file after Fine Uploader adds it to the DOM.

The openLargerPreview function, executed when a user clicks on the thumbnail, looks like this:

function openLargerPreview($scope, $uploadContainer, size, fileId, name) {
    var $modal = $("#previewDialog"),
        $image = $("#previewContainer"),
        $progress = $modal.find(".progress");

    applyNewText("previewTitle", $scope, "Generating Preview for " + name);
    $image.hide();
    $progress.show();

    $modal
        .one("shown.bs.modal", function() {
            $image.attr("src", "");
            $uploadContainer.fineUploader("drawThumbnail", fileId, $image, size).then(function() {
                applyNewText("previewTitle", $scope, "Preview for " + name);

                $progress.hide();
                $image.show();
            },
            function(img, error) {
                $progress.hide();
                applyNewText("previewTitle", $scope, "Preview not available");
            });
        })
        .modal("show");
}

This will display a “Generating” message in the title, along with an animated progress bar in the body of the modal while the preview is generating. The title will be changed to the name of the file & the thumbnail will be displayed once it is ready.

Note that the above code includes a call to an applyNewText function. This function simply ensures the preview title text is updated correctly, since this will happen outside of an Angular digest cycle.

That code looks like this:

function applyNewText(propertyName, $scope, newText) {
    $scope.$apply(function() {
        $scope[propertyName] = newText;
    });
}

Stylized Error Message Dialogs

Error messages, by default, pop up in a native browser alert dialog, which isn’t very appealing from a UI standpoint. We covered how to re-style dialogs in another blog post. Here, we will keep it very simple, and delegate to Bootstrap’s modal component for error dialogs.

An error dialog will appear if you attempt to submit an invalid file, such as one that is too big (on modern browsers) or a file of the wrong type. For example:
error

Here’s the HTML, and the JavaScript:

<div id="errorDialog" class="modal fade">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
                <h4 class="modal-title">Error</h4>
            </div>
            <div class="modal-body">
                <div ng-bind="errorMessage"></div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
            </div>
        </div>
    </div>
</div>
$(element).fineUploader({
    ...
    showMessage: function(message) {
        applyNewText("errorMessage", $scope, message);
        $("#errorDialog").modal("show");
    },
    ...
});

Pretty simple, eh? This is just a basic example. You can, of course, style this modal however you please.

Aggregate Progress Bar

This image uploader example also provides an “aggregate progress bar”. This appears above the file list / drop zone, indicating the total progress of all submitted files.

For example:
progress

The HTML for this is quite simple. We simply include the following anywhere we want in our template script tag:

<div id="totalProgress" class="qq-total-progress-bar-container-selector progress progress-striped active">
    <div class="qq-total-progress-bar-selector progress-bar" role="progressbar"></div>
</div>

No additional JavaScript is required to support total progress as of Fine Uploader 4.4. We simply feed the progress bar using the total progress feature.

Wrap-up

If you want to try all of this out yourself, you can access all source code for this image uploader example in the fine-uploader-example GitHub repo.