Start with the simple case of the CSV file.
With this case, you will loop through query results, and stream straight to the CSV, and perhaps bypass the view.
With Excel, if it really has to be a .xlsx file, then you at the mercy of your Excel lib. Though I think there might be a 65k row limit in Excel. If you can, just export as CSV.
PDF might be in the same boat the the CSV. Use FPDF in the controller, loop through the results (not an array), and stream to the client as soon as you can.