jQueryの非同期通信関数$.ajaxを使用して、サーバ側でのファイルの作成とダウンロードを行う

やりたいことは、左図のようなこと。クライアントからリクエストを送り、サーバ側でダウンロードするファイルを作成(すでにある場合は加工)してクライアントへ返す(ダウンロード)といったこと。
同期通信の場合、クライアントからsubmitボタンを使用して、サーバ側の処理を呼び出し、ダウンロードするファイルをレスポンスデータとしてクライアントへ返すといった、いわゆる普通のダウンロード処理を実装すればよい。ただし、同期通信の場合、レスポンスが返却されるまで、クライアント側の処理が止まってしまう。それを回避すべく、非同期通信でファイルダウンロードを実施しようと考えた。

そこで、jQueryの$.ajax関数を使用して非同期通信によってサーバ側の処理を呼び出してやれば、クライアント側の処理は非同期で継続させられると考えた。しかし、それでは、サーバからクライアントへレスポンスが返却されても、ブラウザがダウンロード用のダイアログを表示してくれなかった。
W3Cのサイトを参照すると、xmlHttpRequestのレスポンスは、テキストベースのレスポンス(responseText)か、XMLベースのDOMオブジェクトでのレスポンス(responseXML)の2つである。そのため、エクセルやパワーポイントファイルダウンロードのレスポンスをハンドリングできない。

そこで、ファイルの作成・加工は非同期通信のリクエストでファイルの作成・加工を行うservletを呼び出し、ダウンロードは非同期通信のコールバック関数の中でダウンロード処理を行うservletを呼び出すようにした。

クライアント側では、非同期通信のコールバック関数で、window.location = "ダウンロード用servletのURL"として、ダウンロードを実行している。

//ファイルダウンロード
		$('#downloadFile').click(function() {
			var _urlForCreatingSheet = "http://localhost:8080/cookie/createLedgerSheet";
			var _urlForDownload = "http://localhost:8080/cookie/downloadFile";
			$.ajax({
				url : _urlForCreatingSheet,
				async : true,
				cache : false,
				success : function(data, dataType) {
                    //ダウンロードのためlocationにダウンロード用コントローラのservletに対応するURLを設定する
					window.location = _urlForDownload;
				}
			});
			alert("asynchronous");
		});

サーバ側のダウンロード用servletのダウンロード部分は、こんな感じ。

protected void printOutFile(HttpServletRequest req,
			HttpServletResponse res, File fileOut) throws ServletException,
			IOException {
		OutputStream os = res.getOutputStream();
		String filePath = "sample.xls";
		try {
			FileInputStream hFile = new FileInputStream(filePath);
			BufferedInputStream bis = new BufferedInputStream(hFile);

			// レスポンス設定
			res.setContentType("application/octet-stream");
			res.setHeader("Content-Disposition",
					"attachment; filename=\"" + fileOut.getName() + "\"");

			int len = 0;
			byte[] buffer = new byte[1024];
			while ((len = bis.read(buffer)) >= 0) {
				os.write(buffer, 0, len);
			}

			bis.close();
		} catch (IOException e) {
			printOutNotFound(res);
		} finally {

			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {

				} finally {
					os = null;
				}
			}
		}
	}

こんなやり方しかないのかな。