HiDefLoLife HiDefLoLife - 5 months ago 63
AngularJS Question

Angular http$ response for text/plain file download fails with SyntaxError: Unexpected number in JSON

I have implemented a download file feature on my Angular-based client and node.js backend based on the following solution: http://stackoverflow.com/a/20904398/1503142. In general this works, but sometimes I receive a "SyntaxError: Unexpected number in JSON at position x" combined with a "TypeError: Cannot read propery 'messages' of undefined".

A few observations:


  • Everything appears to be working on the node server-side, because I do always get a response at client. The error in question is reported by the client; no errors are reported by the server.

  • The log file consists of a time stamp, then basic log information text. The text could contains ASCII characters

  • Using Postman, the response works every time, which lends itself to the idea that it is the http$ code that might be having an issue with the response. Postman's response Header information indicates that the response is Content-Type->text/plain; charset=utf-8.

  • I am using AngularJS v1.2.21 and Node v0.12.13



Here's the client-side code:

$http({
method: 'GET',
url: "/api/logging/logfiles/" + logFile,
headers: { 'Content-Type': 'text/plain;charset=utf-8' }
}).
success(function (data, status, headers, config) {
var anchor = angular.element('<a/>');
anchor.attr({
href: 'data:text/plain;charset=utf-8,' + encodeURIComponent(data),
target: '_blank',
download: logFile
})[0].click();
}).
error(function (data, status, headers, config) {
console.log('Hence my visit to StackOverflow!')
});


Here's the server-side code:

app.get('/api/logging/logfiles/:logfile', function (req, res, next) {
logDirectory = './log';
fs.readFile(logDirectory + "/" + req.params.logfile, 'utf8', function (err, data) {
if (err) {
res.send("Something broke!");
}
else {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
res.send(data);
}
});
});


I suspect that this is related to the contents of the log file. Since I've specified text/plain content, why would there be a JSON parsing error?

Answer

The obvious answer was to encode the file before sending to ensure safe passage, then decode back to file contents at the browser. It appears that some special character(s) was/(were) affecting client-side handling of the incoming data (likely double-quotes):

So on the client-side, I'm now doing this:

$http({
  method: 'GET',
  url: "/api/logging/logfiles/" + logFile
  // Removed unncessary dataType based on charlietfl's comment
}).
success(function (data, status, headers, config) {
  var anchor = angular.element('<a/>');
  anchor.attr({
    // decode the file to go back to raw data
    href: 'data:text/plain;charset=utf-8,' + decodeURI(data),
    target: '_blank',
    download: logFile
  })[0].click();
}).
error(function (data, status, headers, config) {
  console.log('Hence my visit to StackOverflow!')
});

The server-side is:

app.get('/api/logging/logfiles/:logfile', function (req, res, next) {
  logDirectory = './log';
  fs.readFile(logDirectory + "/" + req.params.logfile, 'utf8', function (err, data) {
    if (err) {
       res.send("Something broke!");
    }
    else {
      res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
      // Encode the data to remove characters that might
      // present issues to the client
      res.send(encodeURI(data));
    }
  });
});