When using Angular/WebApi, I found that displaying PDFs in the browser only functions properly if the files are not uploaded to Azure

The Objective: Utilize a POST request to send a PDF file, store its blob content in Azure Storage, and retrieve the content for display in a web browser

Current Progress: The code successfully triggers the controller's POST method with PDF file content, which is then returned as a response that Angular displays in the browser.

Angular/HTML Code:

//html
<object ng-show="content" data="{{content}}" type="application/pdf" style="width: 100%; height: 400px;"></object>

//angular controller
...
.success(function (data) {
   console.log(data);
   var file = new Blob([(data)], { type: 'application/pdf' });
   var fileURL = URL.createObjectURL(file);
   $scope.content = $sce.trustAsResourceUrl(fileURL);
}

WebAPI Controller:

    // POST api/<something>/Upload
    [Authorize]
    [HttpPost]
    [Route("Upload")]
    public async Task<HttpResponseMessage> Post()
    {
        try
        {
            HttpRequestMessage request = this.Request;
            if (!request.Content.IsMimeMultipartContent())
            {
                return new HttpResponseMessage(HttpStatusCode.UnsupportedMediaType);
                //return StatusCode(HttpStatusCode.UnsupportedMediaType);
            }

            var customMultipartFormDataProvider = new CustomMultipartFormDataProvider();

            var provider = await request.Content.ReadAsMultipartAsync<CustomMultipartFormDataProvider>(customMultipartFormDataProvider);
            //contents[1] and //contents[2] were StreamContent of the FormData
            var fileContent = provider.Contents[2];
            var formData = provider.FormData;

            //can succesfully write to a SQL database here without fail

            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
            response.Content = fileContent;
            return response;
    } 

  public class CustomMultipartFormDataProvider : MultipartFormDataRemoteStreamProvider
  {
    public override RemoteStreamInfo GetRemoteStream(HttpContent parent, HttpContentHeaders headers)
    {
        return new RemoteStreamInfo(
            remoteStream: new MemoryStream(),
            location: string.Empty,
            fileName: string.Empty);
    }
  }

The Issue at Hand: Unfortunately, when attempting to upload this content to Azure Storage, it encounters some challenges:

  string blobStorageConnectionString = ConfigurationManager.ConnectionStrings["AzureStorageAccount"].ConnectionString;
  CloudStorageAccount blobStorageAccount = CloudStorageAccount.Parse(blobStorageConnectionString);
  CloudBlobClient blobClient = blobStorageAccount.CreateCloudBlobClient();
  CloudBlobContainer container = blobClient.GetContainerReference(<containerName>);
  container.CreateIfNotExists();
  CloudBlockBlob block = container.GetBlockBlobReference(<keyname>);
  block.UploadFromStream(await fileContent.ReadAsStreamAsync());

While the upload process to storage seems successful and the control flow reaches the return statement in the WebAPI controller, it does not seem to complete.

The "console.log(data)" within my controller's success function never gets triggered. It appears that the return statement may be failing to execute properly despite seeming to do so.

Answer №1

As stated in the comments, it's not possible to use a Stream more than once since they represent a stream of data. One workaround is to first write the stream into a MemoryStream and then utilize this repositioned stream for both Blob upload and response. Here's a simple example:

//copy stream
var ms = new MemoryStream();
await fileContent.CopyToAsync(ms);

//perform other operations

//upload to blob storage
ms.Position = 0;
block.UploadFromStream(ms);

//perform other operations

//set response content
ms.Position = 0;
response.Content = new StreamContent(ms);

Alternatively, consider returning the URI of the uploaded blob to the client instead of the blob content directly. This approach allows the client to decide whether to download the file or ignore it, thereby offloading traffic and bandwidth from your application to Azure Blob service. You can control access to the blob by generating a token with SAS (Shared Access Signature) that has limited time validity:

SharedAccessBlobPolicy sasConstraints = new SharedAccessBlobPolicy();
sasConstraints.SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5);
sasConstraints.SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(2); //two minutes before token expiry
sasConstraints.Permissions = SharedAccessBlobPermissions.Read;

string sasBlobToken = block.GetSharedAccessSignature(sasConstraints);

var blobFinalUri = block.Uri + sasBlobToken;

For further reading:

How do I copy the contents of one stream to another?

Shared Access Signatures, Part 2: Create and use a SAS with Blob storage

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

Using JavaScript to ensure that at least one radio button has been selected

Seeking help with fixing my JavaScript form validation for a newbie like me. I suspect the issue lies within my selector (marked as Not Working Comment) but lack the expertise to resolve it. Trying to implement validation for various input types in my form ...

Developing a fresh feature in Angular.js for transmitting my localstorage model information to a bus?

As a beginner in Angular Js, I have mastered the basics and am now working on storing user input values to localstorage using a form. Although this part is working fine, I need assistance in creating a new service to communicate with my colleague's . ...

Looping through a collection of JSON objects without a parent element

Utilizing jQuery UI Autocomplete, I have encountered a situation where the documentation mentions that the source can consist of a list of JSON objects. The current code is functional; however, it lacks a root element for the list of JSON objects. <scri ...

How can Node.js handle an upgrade request effectively?

Dealing with a websocket 'upgrade' event from a Node.js http server presents an interesting challenge - The upgrade handler takes the form of function(req, socket, head) - Is there a method to respond to this upgrade request without access to a r ...

Alter the reply prior to being dispatched to the customer

Node 16.14.2, Express 4.18.1 There have been several instances where individuals have altered res.send in order to execute actions before the response is sent to the client. app.use(function (req, res, next) { originalSend = res.send; res.send = f ...

Maintain server-side variable state in Next.js after navigating with Link

I have a specific requirement for my NextJS app. I need to render certain pages inside a React Native Web View and hide specific components (like the header and footer) once the rendering is complete. These components are simple, server-side rendered eleme ...

Notify the ajax success if the returned data is null

Is there a way to display an alert message in the AJAX return request if no data is present? I have attempted this in the AJAX success function, but it seems to not be working! This is my current script: <script> $(document).ready(function () ...

Turn various PDF documents into TXT files

I have been looking for a simple solution to convert numerous PDF files into text format, but so far I haven't found an easy way to do it. Could you please suggest the most effective method for achieving this task? I've already tried using some ...

Unable to retrieve the $onAuth property with angularfire

I am currently working on integrating firebase auth into my app. The login controller is functioning properly, but I am facing an issue where the user gets logged out immediately after logging in. .controller('LoginCtrl', function ($scope, $loca ...

What steps should I take to address the issue of fixing the classname rather than using the

<div ng-class:"{{myclass}}" role="progressbar" aria-valuenow="{{roundedtotalPerformanceCount}}" aria-valuemin="0" aria-valuemax="100" ng-style="{'width' : ( totalPerformanceCount + '%' ) }"> {{roundedtotalPerformanceCou ...

Encountered a problem while attempting to start the npm http-server

My project is built on node.js and Angular 2, initially served using lite-server. Now, I need to serve server-side files, so I am switching to http-server. Previously, I used the command "start": "tsc && concurrently \"tsc -w\" \"lite-server ...

Update the HTML form action to use the Fetch API to communicate with the webserver

I successfully managed to store files on my computer by utilizing the HTML form action attribute and then processing this request on my Express webserver. However, when I attempted to switch to using an event listener on the submit button of the form ins ...

Ways to verify a single field within a Zod schema individually rather than the entire schema

I am currently working on a form that consists of multiple inputs, including 'hours' and 'price'. The technology stack I am using is NextJS along with server-side actions. When a user clicks the submit button, here's what happens: ...

Building a follow/unfollow system in Node.jsLet's create a

I am relatively new to programming and I'm looking to implement a follow/unfollow feature in my application. router.put('/user/:id/follow', auth.verifyuser, (req, res)=>{ user.findById(req.params.id) .then((otherUser)=>{ if(otherU ...

What is the best way to display a PDF in a web browser using a JavaScript byte array?

I have a controller that sends the response Entity as a byte array in PDF form to an ajax call. However, I am struggling to display it in the browser despite trying various suggestions from old Stack Overflow questions. Here is the response from the Sprin ...

React - passing down a ref as a prop isn't functioning as expected

In my current project, I am utilizing the react-mui library and aiming to incorporate a MenuList component from the MenuList composition available here. An issue arises when passing a ref as a prop down to a child component containing a menu. For reference ...

A method for accessing a text file from a pastebin using CORS

My goal is to utilize CORS for fetching code snippets from a pastebin and then executing them in a web browser. Here is some work-in-progress code: http://www.example.com/code-snippets.html The code is syntax highlighted with options to run it, etc. I ...

What is the reason for `React.renderToNodeStream` not pausing for the event loop?

Attempting to enhance React's renderToString and renderToStaticMarkup by accommodating the event loop for better server request handling, like so: const React = require('react'); const { renderToNodeStream } = require('react-dom/server& ...

Refreshing the view following a model update within an AJAX call in the Backbone framework

I'm struggling with my code as I can't seem to get my view to update after a model change. var ResultLoanView = Backbone.View.extend({ id:"result", initialize: function(){ this.render(); this.on('submissionMa ...

Mastering the ng-model Directive and Harnessing its Controller in Custom Directives

Recently, I crafted a custom directive that envelops the jstree functionality and incorporated ng-model within the tag of my directive to transmit certain JSON data. I am pondering: Is it necessary for me to utilize ng-model in this particular scenario? ...