Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ngDownloadResponse not working #1

Open
TerrySlack opened this issue May 5, 2015 · 37 comments
Open

ngDownloadResponse not working #1

TerrySlack opened this issue May 5, 2015 · 37 comments

Comments

@TerrySlack
Copy link

I'm having a problem getting the directive to work. To start, I am only using modern versions of FF, Chrome and IE 11. I've included it in the project as per the instructions on github. I've added markup to the html, and added a method to the $scope, within a controller. I can see the encoded response coming back from the server, however in FF, It prompts me to download a corrupt PDF. In Chrome, and IE, nothing happens and there isn't an error. In fact in Chrome and IE, the code jumps to savemethod3 each time. This will open a window and give me a 404. A pdf isn't generated and stored on the server, so the fall back method isn't going to work. I'm wondering, do I have to add responseType: "arraybuffer" to the request....? Is this something that could be built into your directive? And in the markup, you will notice, that I request $event to be the first paramter in getFile, however it is not injected by Angular. I'm wondering if you could also inject the $event. In IE 11, afterwards, the click is treated as an event and it's sent the default route is triggered, resulting in our, case, nothing being displayed on the page, as per the client request. At the bottom I've also included the headers seen in FireFox.

Any feedback is appreciated.

Here is the html markup:

    "

 <li role="presentation">
   <a role="menuitem" tabindex="-1" href="#" download-error=" getfileerror()"
        download-name="{{report.reportName}}.pdf" 
       download-mime="application/pdf"
       download-response="getFile($event, report.reportID, 3)">PDF
   </a>
 </li>

Here is the code from the controller. I should add, it's written in typescript.
$scope.getFile = function (id, fileType): ng.IPromise{
//Build the reportUrl
var promise = undefined,
url = [that.apiBaseService.apiBase];
url.push("/Reports/RunReport/");
url.push(id);
url.push("?OutputFormat=");
url.push("3");
//catch the promise
promise = $http.post(
url.join(""),
that.serializeParameters(),
{ headers: { "Accept": "application/pdf" } });

       //Free resrouces
       url.length = 0;
       return promise;

};
//This is never hit if an error occurs
$scope.getFileError = (aaa,bbb,ccc,ddd,eee,fff,ggg)=> {
var mm = 0;
}

Access-Control-Allow-Orig... *
Access-Control-Expose-Hea...Accept,Origin,X-ProcuraGroup-OutputPageCount
Cache-Control no-cache
Content-Disposition
inline; filename=report.pdf
Content-Length 0
Content-Type application/pdf
Date Tue, 05 May 2015 17:09:03 GMT
Expires -1
Pragma no-cache
Server Microsoft-IIS/8.0
X-AspNet-Version 4.0.30319
X-Powered-By ASP.NET
X-ProcuraGroup-OutputPage... 0
X-SourceFiles =?UTF-8?B?QzpcVXNlcnNcQWRtaW5cRG9jdW1lbnRzXFZpc3VhbCBTdHVkaW8gMjAxM1xQcm9qZWN0c1xNYWluXFByb2N1cmFHcm91cC5BcGlcUmVwb3J0c1xSdW5SZXBvcnRcOA
==?=

@DmitryEfimenko
Copy link
Owner

So the first thing I noticed, you are calling getFile function with 3 arguments:

download-response="getFile($event, report.reportID, 3)"

, but declaring it with two.

function (id, fileType)

I don't think you need to pass $event to the getFile function.
Try fixing that and let me know if this fixes the whole thing.

FYI: Here is some info about formatting things on Github. Use "Preview" tab before commenting :)

@TerrySlack
Copy link
Author

Actually, I forgot to remove that. In my code I do have the 3 parameters for getfile, but removed e from the function params. It passes undefined.

@DmitryEfimenko
Copy link
Owner

If that's the case, I don't see anything wrong with how you use the directive. Lets try to debug what's happening. Use Chrome for this debugging.

Let's see why methods 1 or 2 aren't working in Chrome...
Uncomment all //console.log(e); in the try.. catch blocks in the directive. See what errors you get. If that does not clarify the issue, insert console.log(saveBlob) after line 25. See if you hit it. If you hit it, tell me what is it's value.

@TerrySlack
Copy link
Author

IN IE, in savemethod1, on line 19, it throws an error
var blob = new Blob([data], { type: contentType });
When I examine the data coming in, I noticethe data is an Object ArrayBuffer, but that the byte length is zero. I will contact the dev handling the server side coding and ensure that a pdf is actually being sent back. That being said here is the error for both savemethod1 and savemethod2 printed in the console

InvalidStateError
{
[functions]: ,
proto: { },
ABORT_ERR: 20,
code: 11,
constructor: { },
DATA_CLONE_ERR: 25,
DOMSTRING_SIZE_ERR: 2,
HIERARCHY_REQUEST_ERR: 3,
INDEX_SIZE_ERR: 1,
INUSE_ATTRIBUTE_ERR: 10,
INVALID_ACCESS_ERR: 15,
INVALID_CHARACTER_ERR: 5,
INVALID_MODIFICATION_ERR: 13,
INVALID_NODE_TYPE_ERR: 24,
INVALID_STATE_ERR: 11,
message: "InvalidStateError",
name: "InvalidStateError",
NAMESPACE_ERR: 14,
NETWORK_ERR: 19,
NO_DATA_ALLOWED_ERR: 6,
NO_MODIFICATION_ALLOWED_ERR: 7,
NOT_FOUND_ERR: 8,
NOT_SUPPORTED_ERR: 9,
PARSE_ERR: 81,
QUOTA_EXCEEDED_ERR: 22,
SECURITY_ERR: 18,
SERIALIZE_ERR: 82,
SYNTAX_ERR: 12,
TIMEOUT_ERR: 23,
TYPE_MISMATCH_ERR: 17,
URL_MISMATCH_ERR: 21,
VALIDATION_ERR: 16,
WRONG_DOCUMENT_ERR: 4
}

@TerrySlack
Copy link
Author

Meant to add, it looks like something is being returned in 64 byte encoding
X-SourceFiles =?UTF-8?B?QzpcVXNlcnNcQWRtaW5cRG9jdW1lbnRzXFZpc3VhbCBTdHVkaW8gMjAxM1xQcm9qZWN0c1xNYWluXFByb2N1cmFHcm91cC5BcGlcUmVwb3J0c1xSdW5SZXBvcnRcOA
==?=

This may be why I am prompted in FF to save the file, but when I open it it is corrupted

@DmitryEfimenko
Copy link
Owner

so some sort of data is returned, but the directive fails to instantiate Blob. Seems like something is wring with the data that comes back from the server. What's the server side language that you are using? If it's C# or nodejs, show me the code that assembles file (if you can). I may be able to see what's wrong there. The InvalidStateError that you showed is not really helpful. I don't know how to read it.

@TerrySlack
Copy link
Author

Looks like the server is in fact returning the pdf. Took this from fiddler.

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/pdf
Expires: -1
Server: Microsoft-IIS/8.0
X-ProcuraGroup-OutputPageCount: 0
Content-Disposition: inline; filename=report.pdf
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcQWRtaW5cRG9jdW1lbnRzXFZpc3VhbCBTdHVkaW8gMjAxM1xQcm9qZWN0c1xNYWluXFByb2N1cmFHcm91cC5BcGlcUmVwb3J0c1xSdW5SZXBvcnRcOA==?=
X-Powered-By: ASP.NET
Date: Tue, 05 May 2015 19:41:48 GMT
Content-Length: 7754

%PDF-1.3
1 0 obj
[/PDF /Text /ImageB /ImageC /ImageI]
endobj
6 0 obj
<< /Length 524 /Filter /FlateDecode >> stream
x Kk @� � �ݴ% h sɃ@
#Ȣt Ɗqk[ ء v J � � F ; t ~C WЂ , \A !ָä K $c i � � hV 89 (( �"_q #<� P^e $ O >ޗͬ�}@ � G +) � |
4 � -V � � P $A X�{� $ ߾3 ��R> i�cؠI � ��� �' ͜[�o �Va�F0 ] Ie ٍ c \ -N�d ䷮ �F a R b gR a�r *r wM UG
3 &ζbm
뤜 cΉm+� m
� ^ h| q \ 3 zR jV c iU w LP> ~5? j�{% َKS S/ ك�Y ݠWM� � * 9. ֤U& ; ~7 i �ގK2v� � ~�ֻD�R @ 6� $ Z q } �t Q| m5{ N �:mU % #� \� &� �K ll jPC y �ȇ|[Ro �c 9
endstream
endobj
2 0 obj
<< /Type /Page /Parent 7 0 R /MediaBox [0 0 612.0 792.0] /Contents 6 0 R /Resources << /ProcSet 1 0 R /XObject << /Im5 5 0 R >> /Font << /F3 3 0 R /F4 4 0 R >> >> >>
endobj
5 0 obj
<< /Type /XObject /Subtype /Image /BitsPerComponent 8 /Filter /DCTDecode /ColorSpace /DeviceRGB /Width 182 /Height 83 /Length 5796 >>
stream
�JFIF ��� C �������������������������� � � �
��

��

C����������� � �

@TerrySlack
Copy link
Author

So we are back to looking into why it breaks on line 19, in savemethod1, using this data.....

@DmitryEfimenko
Copy link
Owner

yeah, it does look like a pdf.
add console.log(data) before line 19 and see what it's value there.

@TerrySlack
Copy link
Author

will do, while I wait, would this have anything to do with it...search for InvalidStateError
angular/angular.js#1922

@TerrySlack
Copy link
Author

K, here is the data written out to the console

@TerrySlack
Copy link
Author

config

Object { method="POST",  transformRequest=[1],  transformResponse=[1],  more...}

data

[Object { ParameterID=1,  ParameterValue="5"}, Object { ParameterID=2,  ParameterValue="4/3/2015"}, Object { ParameterID=3,  ParameterValue="5/2/2015"}]

0

Object { ParameterID=1,  ParameterValue="5"}

ParameterID

1

ParameterValue

"5"

1

Object { ParameterID=2,  ParameterValue="4/3/2015"}

ParameterID

2

ParameterValue

"4/3/2015"

2

Object { ParameterID=3,  ParameterValue="5/2/2015"}

ParameterID

3

ParameterValue

"5/2/2015"

headers

Object { Content-type="application/json",  Accept="application/pdf",  Authorization-Token="bfff00c1-aa54-4100-af4e-26829f3d5711"}

Accept

"application/pdf"

Authorization-Token

"bfff00c1-aa54-4100-af4e-26829f3d5711"

Content-type

"application/json"

method

"POST"

responseType

"arraybuffer"

transformRequest

[function(d)]

0

function(d)

transformResponse

[defaultHttpResponseTransform(data, headers)]

0

defaultHttpResponseTransform(data, headers)

url

"http://localhost:63691/Reports/RunReport/8?OutputFormat=3"

data

ArrayBuffer {}

status

200

statusText

"OK"

headers

function(name)

@DmitryEfimenko
Copy link
Owner

could you please show me $scope.getFile function (formatted well so that I can clearly read it)

@TerrySlack
Copy link
Author

var that = this;
$scope.getFile = function(e, id, fileType): ng.IPromise < any > {
//Build the reportUrl
var promise = undefined,
url = [that.apiBaseService.apiBase];

//Prevent the postback
//e.preventDefault();

url.push("/Reports/RunReport/");
url.push(id);
url.push("?OutputFormat=");
url.push("3");

//Get the element from the event, and look at the data on it and find otu the mime type..I can use this to determine the number to pass for outputformat
//catch the promise
//promise = $http.post(url.join(""), that.serializeParameters(), { headers: { "Accept": "application/pdf" }/*, responseType: "arraybuffer"*/ });

//catch the promise
promise = $http({
    url: url.join(""),
    method: "POST",
    data: that.serializeParameters(), //this is your json data string
    headers: {
        "Content-type": "application/json",
        "Accept": "application/pdf"
    },
    responseType: "arraybuffer"
});

//Free resrouces
url.length = 0;
return promise;

};

@TerrySlack
Copy link
Author

{
"data":{

},
"status":200,
"config":{
"method":"POST",
"transformRequest":[
null
],
"transformResponse":[
null
],
"url":"http://localhost:63691/Reports/RunReport/8?OutputFormat=3",
"data":[
{
"ParameterID":1,
"ParameterValue":"5"
},
{
"ParameterID":2,
"ParameterValue":"4/3/2015"
},
{
"ParameterID":3,
"ParameterValue":"5/2/2015"
}
],
"headers":{
"Content-type":"application/json",
"Accept":"application/pdf",
"Authorization-Token":"bfff00c1-aa54-4100-af4e-26829f3d5711"
},
"responseType":"arraybuffer"
},
"statusText":"OK"
}

@TerrySlack
Copy link
Author

the above is the data object formatted properly....

@TerrySlack
Copy link
Author

It appears that the data object is the request object isn't it? Shouldn't it be part of the response

@TerrySlack
Copy link
Author

I think I found the problem. Data is a wrapper for the whole response and not just the 'data' that is returned. On line 96 I added the following line: var processData = data.data;
and in IE and Chrome a proper pdf is returned.

However, in FireFox, no prompt comes up to save a pdf and in the console I see the following
[14:29:26 GMT-0600 (Mountain Standard Time)] Browser Link: Failed to match method call MadsKristensen.EditorExtensions.BrowserLink.Menu.MenuBrowserLinkFactory.setVisibility

[14:29:26 GMT-0600 (Mountain Standard Time)] Browser Link: Failed to match method call MadsKristensen.EditorExtensions.BrowserLink.PixelPushing.PixelPushingModeFactory.setPixelPusingMode

@DmitryEfimenko
Copy link
Owner

This is exactly what I was thinking. The data object should have been the actual pdf stream... not the http response. I'm not sure how this even worked for me before because I'm using the
promise.then() on line 85, in which case it needs to be data.data - just like you said.
If I used promise.success(data) instead, then just data would return the right thing.
Something must have changed with the way angular promises work.

Anyway, the error that you are getting in FF seems to be unrelated to ngDownloadResponse directive. There are no methods there called setVisibility or setPixelPusingMode. This is from another plugin. Try do a global search on your project for these functions.

@TerrySlack
Copy link
Author

Did some more digging and in FF, the arraybuffer is empty in data.data...and yes, I did some research and setVisibility or setPixelPusingMode are not related to the issues we are having...

So what do you think the solution is then. Everything works fine in Chrome and IE with data.data, but not in FF.

@DmitryEfimenko
Copy link
Owner

not sure if this is related, but I usually put this line of code in the angular.config() function:

delete $httpProvider.defaults.headers.common['X-Requested-With'];

What is the value of data.data in FF? console.log() it

@TerrySlack
Copy link
Author

I'm unable to delete an headers unfortunately....as mentioned two comments up, data.data is an empty arraybuffer in FF. So the stream may be somewhere else within data. Are you able to investigate?

@DmitryEfimenko
Copy link
Owner

I have tested the directive on Chrome, IE, and FF on my side. It works as expected.
I'm unsure why data is coming back differently from the server when using FF. This should not depend on the browser at all.

@TerrySlack
Copy link
Author

ok, were you working with a stream or pulling a pdf sitting on a server?

@DmitryEfimenko
Copy link
Owner

I was working with a stream. Though I used sockets instead of $http. Here is my function to get data:

$scope.getPdf = function () {
    var deferred = $q.defer();
    socket.emit('getPdf', $scope.args, function(response) {
        if (response != false) {
            deferred.resolve(response);
        } else {
            deferred.reject();
        }
    });
    return deferred.promise;
}

@TerrySlack
Copy link
Author

Thanks for that...Ill try it out in the morning...did you use a library for the socket? Ive not worked with sockets before
Terry Slack
Manager, IT
Fitrix Mobile
Mobile: (780) 243 4994
FitrixMobile.com


From: Dmitry A. Efimenko [email protected]
Sent: Tuesday, May 5, 2015 6:24:00 PM
To: DmitryEfimenko/ngDownloadResponse
Cc: Terry Slack
Subject: Re: [ngDownloadResponse] ngDownloadResponse not working (#1)

I was working with a stream. Though I used sockets instead of $http. Here is my function to get data:

$scope.getPdf = function () {
var deferred = $q.defer();
socket.emit('getPdf', $scope.args, function(response) {
if (response != false) {
deferred.resolve(response);
} else {
deferred.reject();
}
});
return deferred.promise;
}

Reply to this email directly or view it on GitHubhttps://github.com//issues/1#issuecomment-99270927.

@DmitryEfimenko
Copy link
Owner

I'm not suggesting you switching to sockets just to solve this issue. It was just an example of the code that showed how I handled returning a promise. You could try something similar for your $http:

$scope.getPdf = function () {
    var deferred = $q.defer();
    $http.post('getPdf', $scope.args)
        .success(function(response){
            deferred.resolve(response);
        })
        .error(function(){
            deferred.reject();
        });
    return deferred.promise;
}

@TerrySlack
Copy link
Author

I was digging around today, and using Fiddler, and I noticed that $http gets called twice in FF, but only once in Chrome and IE. It looks like the first call is cancelled by the second call, but the promise returned from the first call is handled by your directive....in the second call, the arraybuffer is populated and has a length. The promise that is handled by the directive, the arraybuffer has a bytelength of zero. Ever run into anything like this?

Terry Slack
Manager, IT
Fitrix Mobile
Mobile: (780) 243 4994
FitrixMobile.com


From: Dmitry A. Efimenko [email protected]
Sent: Wednesday, May 6, 2015 11:24 AM
To: DmitryEfimenko/ngDownloadResponse
Cc: Terry Slack
Subject: Re: [ngDownloadResponse] ngDownloadResponse not working (#1)

I'm not suggesting you switching to sockets just to solve this issue. It was just an example of the code that showed how I handled returning a promise. You could try something similar for your $http:

$scope.getPdf = function () {
var deferred = $q.defer();
$http.post('getPdf', $scope.args)
.success(function(response){
deferred.resolve(response);
})
.error(function(){
deferred.reject();
});
return deferred.promise;
}

Reply to this email directly or view it on GitHubhttps://github.com//issues/1#issuecomment-99543682.

@TerrySlack
Copy link
Author

I did some research and it looks as though this has been an issue with a get. I'm using a post
angular/angular.js#1981?

@DmitryEfimenko
Copy link
Owner

That seems to be a very old issue (2013). Make sure to use latest angular. Could you show me the whole html element which uses the directive?

@TerrySlack
Copy link
Author

we are using the latest angular v1.3.15. I'm not sure how to do the markup for the html in here. Also, this is why I want the event passed in in order to test if e.preventDefault can not only prevent our router from being called, but perhaps FF is treating it as being clicked twice. I will email you the markup to take a look at

@TerrySlack
Copy link
Author

Here is one anchor that I use to call for a pdf. I have two others which will call excel or word. They are all marked up the same. At thsi time, $event does not get injected into the getFile method.

PDF

@TerrySlack
Copy link
Author

I have issued a pull request regarding the use of the event object in the getHandler method. In my case, I found that I needed it to prevent a default route from being triggered, when downloading a pdf.

It's my first pull request, I see I can merge it automatically, however I will wait until I hear back from you regarding this.

Strangely enough, doing a reboot seemed to solve my problem, as I was able to download PDF, Excell and Word docs using your directive.

Thanks for the help and the directive.


From: Dmitry A. Efimenko [email protected]
Sent: Wednesday, May 6, 2015 11:53 AM
To: DmitryEfimenko/ngDownloadResponse
Cc: Terry Slack
Subject: Re: [ngDownloadResponse] ngDownloadResponse not working (#1)

That seems to be a very old issue (2013). Make sure to use latest angular. Could you show me the whole html element which uses the directive?

Reply to this email directly or view it on GitHubhttps://github.com//issues/1#issuecomment-99552130.

@DmitryEfimenko
Copy link
Owner

To avoid issues with angular trying to navigate to other routes try using a <button> instead of an <a>:

<button type="button" class="btn btn-link"
        download-backup-url="/Reports/Report/{{report.reportID}}"
        download-error=" getfileerror()"
        download-name="{{report.reportName}}.pdf"
        download-mime="application/pdf"
        download-response="getFile(report.reportID, 3)">PDF
</button>

Couple things to notice here:

  • type="button" attribute is applied to make sure this button isn't sending any extra events and thus only does the download-response functionality
  • The $event parameter is removed from getFile() due to above. You can also remove .preventDefault() from your js code.
  • class="btn btn-link" is applied to style this button as a link (if you use Bootstrap. If not you can steal styles from them).

@TerrySlack
Copy link
Author

could we have an option for the event object so we aren't limited to using buttons?

@DmitryEfimenko
Copy link
Owner

try editing line 83 of the directive to:

$element.on('click', function(e) {

and add right after:

e.preventDefault();

Let me know if this works for your scenario.

@TerrySlack
Copy link
Author

It does work...did that originally, but then decided to add to the getHandler method because their are other things you could do technically with the event object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants