Thanks for taking your time to read this question.
What do I need:
We have a SOAP web service hosted somewhere that will take a couple of parameters and generate reports by using SQL Server Reporting Services. The format of the report file could be a couple including excel and pdf. We don't own that service and we cannot change it.
We need a web app that calls the SOAP web service I mentioned above, generate a report, and stream the file back to the browser.
It has to be a web api 2/ angularjs app.
What do I have:
So far I got client calling web api no problem, and i can see the result all the way back from the SOAP web service, but I cannot download the file, or even I got the file downloaded, it's either saying the file is corrupted or the file is blank.
My Code:
-server side:
var reportResponse = new RequestResponse();
try
{
reportResponse = client.RenderReport(reportHeader);
if (!reportResponse.Successful)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(reportResponse.ErrorMessage)
};
}
var stream = new MemoryStream(reportResponse.Result);
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = outputName + fileExtension;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return response;
}
catch (Exception exception)
{
return new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(exception.Message)
};
}
-client side:
function getReport(request) {
return $http.get('/api/reports/', {
params: request
}, { responseType: 'arraybuffer' })
.then(
function (result) {
var octetStreamMime = 'application/octet-stream';
var success = false;
// Get the headers
var headers = result.headers;
// Get the filename from the x-filename header or default to "download.bin"
var filename = headers['x-filename'] || 'download.bin';
// Determine the content type from the header or default to "application/octet-stream"
var contentType = headers['content-type'] || octetStreamMime;
try {
// Try using msSaveBlob if supported
console.log("Trying saveBlob method ...");
var blob = new Blob([result.data], { type: contentType });
if (navigator.msSaveBlob)
navigator.msSaveBlob(blob, filename);
else {
// Try using other saveBlob implementations, if available
var saveBlob = navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
if (saveBlob === undefined) throw "Not supported";
saveBlob(blob, filename);
}
console.log("saveBlob succeeded");
success = true;
} catch (ex) {
console.log("saveBlob method failed with the following exception:");
console.log(ex);
}
if (!success) {
// Get the blob url creator
var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
if (urlCreator) {
// Try to use a download link
var link = document.createElement('a');
if ('download' in link) {
// Try to simulate a click
try {
// Prepare a blob URL
console.log("Trying download link method with simulated click ...");
var blob = new Blob([result.data], { type: contentType });
var url = urlCreator.createObjectURL(blob);
link.setAttribute('href', url);
// Set the download attribute (Supported in Chrome 14+ / Firefox 20+)
link.setAttribute("download", filename);
// Simulate clicking the download link
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
link.dispatchEvent(event);
console.log("Download link method with simulated click succeeded");
success = true;
} catch (ex) {
console.log("Download link method with simulated click failed with the following exception:");
console.log(ex);
}
}
if (!success) {
// Fallback to window.location method
try {
// Prepare a blob URL
// Use application/octet-stream when using window.location to force download
console.log("Trying download link method with window.location ...");
var blob = new Blob([result.data], { type: octetStreamMime });
var url = urlCreator.createObjectURL(blob);
window.location = url;
console.log("Download link method with window.location succeeded");
success = true;
} catch (ex) {
console.log("Download link method with window.location failed with the following exception:");
console.log(ex);
}
}
}
}
if (!success) {
// Fallback to window.open method
console.log("No methods worked for saving the arraybuffer, using last resort window.open");
window.open(httpPath, '_blank', '');
}
},
function (error) {
logger.error('Error getting report', error);
})
.catch(function (error) {
logger.error('Error getting report', error);
});
}
For the client side code, I referenced this post. I tried to get a report in pdf, and I did get the file saved on the client side named download.bin. I can take a look at the header and figure it out, but I don't think that's the problem. In this case, download.bin can be opened with Adobe Reader, and it even knows it's a two page pdf file, but it shows blank.
I tried to save the file from my api controller, it has no problem at all, which means the web service I called has no problem.
I use chrome to check the get response, in my result.data, it is something like this(I delete some of the chars between "stream" and "endstream"):
[/PDF /Text /ImageB /ImageC /ImageI]
endobj
6 0 obj
<< /Length 3655 /Filter /FlateDecode >> stream
xË·÷ô
D;iÞ/ÚÙpdØâdg1I$=9ÐÛ§Èî.Vߦx`ñÿë;d7Éfw|òÇ`¼CBÛ48+ágJ
·§áÃÛHuèoRÁ%'btVQxí .þúfª¦Zv¬eæZEà«ÃOÌ äðâ×AÿuZO¥HJ^jÒÃWç?\}xsz{7ützyºþóôjøúêîôÙðâ?ÿ{ñàë·³®ªõy7Æ
wàýË77ïI
endstream
endobj
8 0 obj
<< /Type /Page /Parent 7 0 R /MediaBox [0 0 612.0 792.0] /Contents 9 0 R /Resources << /ProcSet 1 0 R /XObject << /Im5 5 0 R >> /Font << /F3 3 0 R /F4 4 0 R >> >> >>
endobj
3 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>
endobj
4 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>
endobj
7 0 obj
<< /Type /Pages /Kids [ 2 0 R 8 0 R ] /Count 2 >>
endobj
10 0 obj
<< /Type /Catalog /Pages 7 0 R >>
endobj
11 0 obj
<< /Title <feff004700520050005f00410053004f005000610079006d0065006e0074>
/Author <>
/Subject <>
/Creator (Microsoft Reporting Services 10.0.0.0)
/Producer (Microsoft Reporting Services PDF Rendering Extension 10.0.0.0)
/CreationDate (D:20150922094257-04'00')
>>
endobj
xref
0 12
0000000000 65535 f
0000000010 00000 n
0000003799 00000 n
0000079131 00000 n
0000079236 00000 n
0000003983 00000 n
0000000065 00000 n
0000079336 00000 n
0000078947 00000 n
0000077371 00000 n
0000079404 00000 n
0000079457 00000 n
trailer << /Size 12 /Root 10 0 R /Info 11 0 R >>
startxref
79744
%%EOF
If I understand this correctly, it could very well be an encoding problem. I tried to use Uint8Array() for creating the blob, it did not work.
I also tried to use FileSaver.js, and got the same thing.
Could someone point me to the right direction? Thanks so much.