Item 2 seems well separable from whether you use XHR or not. Although the “X” in XHR is XML, these days you can set a so-called responseType attribute to have it give you a binary file.
And then item 3 is when things get a little weird. XHR loads stuff into memory in JS on the client. There’s nothing that the server could do to prompt the user to download the file in this design. It would have to be something you do on the client side. And the specifics of how to do that are shrouded in mystery, with the pratctical solution being “use a library that someone else wrote after they painstakingly studied the behaviors of a dozen different browsers.” Here’s one I found in a web search: GitHub - eligrey/FileSaver.js: An HTML5 saveAs() FileSaver implementation