How to Retrieve a Remote File in Angular using $http.get() with OAuth Authentication

I have a unique situation where my users possess private files that require downloads by authenticated users. The server I am using initially downloads a file from S3 utilizing its own set of S3 app_id and secret_token credentials. Once the file has been downloaded, it is then compiled and transmitted to the client using Rails' send_data method.

Here's how it looks in Ruby on Rails:

# documents_controller.rb
def download
  some_file = SomeFile.find(params[:id])

  # download file from AWS S3 to server
  data = open(some_file.document.url) 

  # construct and send downloaded file to client
  send_data data.read, filename: some_file.document_identifier, disposition: 'inline', stream: 'true'
end

Initially, my plan was to trigger the download directly from the HTML template as shown below:

HTML:

<!-- download-template.html -->
<a target="_self" ng-href="{{ document.download_url }}" download="{{document.file_name}}">Download</a>

Although this seemed straightforward, I encountered an issue with Angular's $http interceptor not intercepting external link clicks, resulting in missing headers needed for server-side authentication. This led to a 401 Unauthorized Error.

To overcome this challenge, I decided to trigger the download using ng-click and initiating an $http.get() request from the angular controller instead.

HTML:

<!-- download-template.html -->
<div ng-controller="DocumentCtrl">
  <a ng-click="download(document)">Download</a>
</div>

Javascript:

// DocumentCtrl.js
module.controller( "DocumentCtrl",
  [ "$http", "$scope", "FileSaver", "Blob",
  function( $http, $scope, FileSaver, Blob ) {

    $scope.download = function( document ) {
      $http.get(document.download_url, {}, { responseType: "arraybuffer" } )
        .success( function( data ) {
          var blob = new Blob([data], { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" });
          FileSaver.saveAs(blob, document.file_name);
        });
    };
}]);

FileSaver is a useful library for saving files using Blobs on the client side.

This approach helped me resolve the authentication problem, but unfortunately, the downloaded file ended up being saved/downloaded in an unreadable or unusable format.

What could be causing the file to download in an unusable format?

Thank you in advance.

Answer №1

To successfully retrieve binary data as a response in Angular, it is necessary to configure the $http method accordingly.

Referencing Rails' send_data documentation:

The send_data method allows for sending binary data to the browser. It provides options for displaying the response as either an attachment or inline content, specifying content type, file name, and more.

In Angular's $http documentation, there is limited information available on configuring responseType for handling binary data responses. Essentially, responseType needs to be set to "arraybuffer" to indicate that the response will contain binary data (as shown below).

$scope.download = function(document) {
  console.log("download: ", document);
  $http({
    url: document.download_url,
    method: "GET",
    headers: {
      "Content-type": "application/json"
    },
    responseType: "arraybuffer" // expect to handle binary data response
  }).success(function(data, status, headers) {
      var type = headers('Content-Type');
      var blob = new Blob([data], {type: type});
      FileSaver.saveAs(blob, document.file_name);
    });
};

It would be beneficial for Angular's $http documentation to provide more detailed explanations rather than just listing the basic usage and arguments.

Usage

$http(config);

Arguments

config

responseType - {string} - see XMLHttpRequest.responseType.

Answer №2

Hey there! I wanted to share an example of how I handle file downloads from my server using Angular:

First, I make a GET request to retrieve the file:

Here is the HTML code for initiating the file download on the client side:

<a ng-href="/api/downloadFile/{{download.id}}" type="submit" class="btn btn-primary col-lg-12 btn-modal-costume" >Download</a>

And here is the Java code to handle the file download on the server side:

public static Result download(String id) {
        String content = null;
        for (controllers.file file : files) {
            if (file.getId().equals(id)){
                content = file.getContent();
            }
        }
        return ok(new java.io.File("/temp/" + id+ "file" + content)).as("application/force-download");
    }

If you're interested, you can access the full code in my GitHub project

Answer №3

It seems like you were heading in the right direction with your approach using JavaScript, but there was a small mistake. Instead of passing an empty object as the second parameter in the $http.get call, you should have included the options argument with {responseType: arraybuffer}. For more information, refer to the documentation for $http.get available at: https://docs.angularjs.org/api/ng/service/$http#get

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

A step-by-step guide on how to implement a window scroll-controlled color transition

I've implemented jQuery code to change the opacity of my navbar's background as the user scrolls, transitioning from transparent to blue. Here's the snippet: $(window).scroll(function(){ var range = $(this).scrollTop(); var limit = 45 ...

"Provide information, then open a new webpage by clicking on a button. Perform various actions with the form by using type

Is there a way to quickly submit form entries to my database and then automatically redirect to a new page? My current situation requires that submitted information is stored in the DB before directing users to a thank you page, all in one seamless click! ...

Utilizing Bootstrap Modal to trigger an Action Result function when a button is clicked

I could use some assistance. In my current setup, I have a file picker that loads a file into a specific table in my database. When the user clicks a button, a bootstrap modal pops up to ask if they are uploading more files. This is how it looks in my vi ...

What is the best way to replicate the Ctrl+A action on an element using jQuery or JavaScript?

Is there a way to trigger the Ctrl+A key combination in a textarea element when a specific event occurs, without resorting to caret positioning or selecting all text separately? I'm looking for a method that simulates hitting Ctrl+A using a cross-brow ...

Is there a way to detect when the mobile keyboard is open in a React application?

I am currently working with a Textfield that includes the Autofocus attribute. I am wondering if there is a method to detect when the keyboard opens in mobile view and then store this information in a boolean variable. https://i.stack.imgur.com/z0EtB.png ...

Is there a way to incorporate an MTN Momo or Orange Money payment system into your application if you are not a registered business entity?

Implementing an MTN Momo and Orange Money payment system through their respective APIs is crucial. In addition, I am seeking a dependable and effective method to seamlessly integrate these diverse APIs. During my attempt to incorporate the API via their ...

What are the steps to run a webpack project without relying on webpack-dev-server?

I've been working on hosting my project on GitHub pages by creating a /doc file and placing all my HTML, CSS, and JS there. If you're interested, you can check out my project here: https://github.com/mattfrancis888/the_movie_db The only way I&a ...

Pause a YouTube video automatically when the window is not in focus using FancyBox

I have successfully implemented dynamic play/pause functionality for a YouTube video, as demonstrated in my weave. However, I am facing challenges in making it work dynamically with FancyBox using the onBlur event (to pause the video when the window is no ...

Tips for inserting a php variable into an html document

I am trying to figure out how to export a variable from a php file into an html file. The php file I'm working with is example.php and it contains the following code: <?php $array= ['one','two']; ?> The html file is named ...

Understanding the mechanics of utilizing node modules and requiring them within an Express 4 router

After initiating a node project using an express 4 generator, I have set up the following routing code in the /routes/index.js file: // ./routes/index.js var express = require('express'); var router = express.Router(); router.get('/' ...

Exploring the Power of Streams in JavaScript

I'm looking to create a stream object that can perform the following actions: // a -------1------2----3 // map -----\------\----\ // b --------2------4----6 const a = new Stream(); const b = a.map(value => value * 2); b.subscribe( ...

Achieve consistent action execution for both the footer and header included in all pages using AngularJS

This particular query has a significant possibility of being considered a duplicate, but in my attempts to find a satisfactory explanation for my problem through various searches, I have not been successful - so please accept my apologies if this is indeed ...

Simple Way to Show API Output in Plain Text (Beginner Inquiry)

I'm a beginner when it comes to using APIs. I'm trying to display the plain text response from this URL on my webpage: However, I'm encountering issues with getting it to work. Here's my code: <script src="https://ajax.googleap ...

Generating a default template for an Angular ag-Grid cell with a custom field name - how to do it?

I am currently working with ag-grid and have specific templates for many columns. However, some of the data I am inputting into the table are just plain text. I want to enhance the default case: <ng-template #defaultRecord let-record> ADDITIONAL E ...

Embed script tags into static HTML files served by a Node.js server

Looking to set up a Node server where users can request multiple pages from a static folder, and have the server inject a custom tag before serving them. Has anyone had success doing this? I've tried with http-proxy without success - not sure if a pro ...

What is the method for invoking a custom directive's function in AngularJS?

When working with AngularJS, you have the ability to create a button that triggers an action. Here's an example: <div ng-controller="myController"> <button ng-click="onButtonClicked()">Click me</button> </div> Now, let&apos ...

Include a new variable in a JavaScript email template

Is there a way to include the variable bob in the body of an email? var bob; function sendMail() { var link = "mailto:YourEmailHere" + "?cc=" + "&subject=App Build Link Buit With MWFPRO's App Build Tool" + "&body=Hi ...

I am experiencing an issue with the functionality of Handlebars registerPartial()

Check out my code snippet on JsFiddle Error Message: Uncaught Error - The partial social could not be found Note: I have made sure to include all necessary libraries. Looking forward to your assistance. Thank you! ...

Identifying sluggish hardware or unresponsive browsers using JavaScript

My site features numerous CSS animations and transitions that run very slowly on specific browsers and older hardware. I want to avoid user-agent sniffing; is there a way to identify browsers or hardware configurations using JavaScript or a JS library, and ...

Is there a way to execute a jar file using JavaScript?

Hello, I'm new to this and still learning. I have a jar file that takes one parameter and returns a JSON with the result. Could someone please advise me on how to run my jar using JavaScript? I've attempted the following code but it's not wo ...