質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
ASP.NET MVC 4

ASP.NET MVC4は、MVCパターンを利用して、高度なテスト機能と保守機能を備えた Web アプリケーションを開発するためのフレームワークです。

Q&A

解決済

1回答

12071閲覧

[openXML]ASP.NETでダウンロードさせたExcelが開けない。

shikasama

総合スコア163

ASP.NET MVC 4

ASP.NET MVC4は、MVCパターンを利用して、高度なテスト機能と保守機能を備えた Web アプリケーションを開発するためのフレームワークです。

0グッド

0クリップ

投稿2016/09/01 01:13

編集2016/09/07 07:23

###[openXML]ASP.NETでダウンロードさせたExcelが開けない。
Templateに追記したファイルをダウンロードさせるため、
まず編集せずにダウンロードさせようと思いました。
Excelの操作にはopenXMLを使用しています。
ダウンロードはできましたが、肝心のファイルが開けませんでした。ご教示ください。

###発生している問題・エラーメッセージ
ダウンロードしたファイルを開こうとすると以下のエラーが発生します。

Excelでファイル'ダウンロードファイル名.xlsx'を開くことができません。 ファイル形式またはファイル拡張子が正しくありません。 ファイルが破損しておらず、ファイル拡張子とファイル形式が一致していることを確認してください。

###該当のソースコード

C#

1public ActionResult DownloadExcel() 2{ 3 // xlsxドキュメントを開く。 4 // 第2引数:編集するかどうか。 5 using (SpreadsheetDocument document = SpreadsheetDocument.Open(Server.MapPath("~/App_Data/Template.xlsx"), true)) 6 using(MemoryStream ms = new MemoryStream()) 7 { 8 // Workbookにアクセス。 9 WorkbookPart wbPart = document.WorkbookPart; 10 11 // シート情報を取得 12 Sheet targetSheet = document.WorkbookPart.Workbook.Sheets.Elements<Sheet>().First(); 13 if (targetSheet == null) 14 { 15 return null; 16 } 17 18 // シート情報からIDを取得し、WorksheetPartを得る 19 WorksheetPart wsPart = (WorksheetPart)(wbPart.GetPartById(targetSheet.Id)); 20 21 // ワークブックを保存 22 wsPart.Worksheet.Save(ms); 23 return File(ms.ToArray(), "application/msexcel", "ダウンロードファイル名.xlsx"); 24 } 25} 26

###試したこと
wsPart.Worksheet.Save(ms)したあとにmsが閉じてしました。
↓を参考にしました。
http://xptn.dtiblog.com/blog-entry-19.html

C#

1wsPart.Worksheet.Save(ms); 2ms.Close(); 3return File(ms.ToArray(), "application/vnd.ms-excel", "ダウンロードファイル名.xlsx");

が、変わらずダウンロードしたファイルは壊れて開けませんでした。

msが閉じていることはあまり関係ないみたいでした。
http://xptn.dtiblog.com/blog-entry-19.html

MemoryStreamを使わずに直接ダウンロードさせたら開けました。
なので、MemoryStreamにドキュメントをセーブした時点で中身が壊れてしまったものだと思われます。

###解決
以下の様にすることでメモリ上でExcelファイルの編集を行い、一時ファイルを作成することなくクライアントにダウンロードさせることができました。

C#

1public ActionResult DownloadExcel() 2{ 3 // テンプレートファイルのパス 4 var templateName = Server.MapPath("~/App_Data/Template.xlsx"); 5 6 // ファイルをbyte配列に変換 7 var byteArray = System.IO.File.ReadAllBytes(templateName); 8 using (MemoryStream ms = new MemoryStream()) 9 { 10 /* 11 * MemoryStreamにbyte配列を書き込む 12 * MemoryStream上にExcelドキュメントができる 13 */ 14 ms.Write(byteArray, 0, (int)byteArray.Length); 15 // MemoryStream上のExcelドキュメントを開く 16 using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Open(ms, true)) 17 { 18 WorkbookPart wbPart = spreadSheet.WorkbookPart; 19 20 Sheet targetSheet = spreadSheet.WorkbookPart.Workbook.Sheets.Elements<Sheet>().First(); 21 if (targetSheet == null) 22 { 23 return null; 24 } 25 26 WorksheetPart wsPart = (WorksheetPart)(wbPart.GetPartById(targetSheet.Id)); 27 28 /* 29 * ここでいろいろExcelファイルを編集する 30 */ 31 32 wsPart.Worksheet.Save(); 33 } 34 return File(ms.ToArray(), "application/ms-excel", "ダウンロード時のファイル名.xlsx"); 35 } 36}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

twck

2016/09/01 13:38

ASP.NETではなくopenXMLの問題ではないかと思うので、質問のタイトルやタグに「openXML」を入れた方が見つけてもらいやすくなりますよ。 あと、openXMLの代わりにFileStreamで読み込んでMemoryStreamに書き込む方法を試してみて、その結果を「試したこと」に追記すれば問題がASP.NETとopenXMLのどちらなのかを切り分ける情報になると思います。
shikasama

2016/09/02 08:21

twck様 アドバイスありがとうございました。
twck

2016/09/02 08:44

私が回答までできればよいのですが、私はopenXMLを使ったことがないのですみません。私はopenXMLではなくNPOIを使用してEXCELを編集しているのですが、よくミスってファイルが破損するので、おそらくASP.NETではなくopenXMLの使い方に問題があると思います。
shikasama

2016/09/05 02:50

twck様 お気遣いありがとうございます。 もう少し調べてみようと思います。
guest

回答1

0

ベストアンサー

どうやら teratail には Open XML SDK の利用者がいないようなので、導入して少し調べてみました。

C#

1// ワークブックを保存 2wsPart.Worksheet.Save(ms);

質問文に出てきたこの部分はワークブックではなくワークシートの出力を行っているようです。つまり Template.xlsxファイルの中の一部分しか出力されない。じゃあ Workbook.Saveならどうだとやってみましたが、今度はワークブックの基本情報だけ出力されて、ワークシートが出力されない。しばらく調べてみたのですが、どうにも全体をMemoryStreamに書き出す方法が見つからない。

開いたファイルを書き換えるのは普通にできるので、ファイルの代わりにMemoryStreamから開いてみたらどうだろうとやってみたら出来ました。

C#

1// テンプレートファイルのパス 2var filename = Server.MapPath("~/App_Data/Template.xlsx"); 3 4// ファイルをMemoryStreamに読み込み 5var bytes = System.IO.File.ReadAllBytes(filename); 6using (MemoryStream ms = new MemoryStream(bytes)) 7{ 8 // MemoryStream上のExcelドキュメントを開く 9 using (SpreadsheetDocument document = SpreadsheetDocument.Open(ms, true)) 10 { 11 // 編集する 12 } 13 14 return File(ms.ToArray(), "application/ms-excel", "ダウンロード時のファイル名.xlsx"); 15}

これなら Template.xlsx を読み込んで、編集して、送信する、ということができそうですがどうでしょうか?

投稿2016/09/07 05:37

編集2016/09/07 07:59
twck

総合スコア314

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

twck

2016/09/07 06:29

最初は出来ないと書きましたが、出来たっぽいので回答を書き直しました。
shikasama

2016/09/07 07:12

twck様 ありがとうございます。 わざわざ導入していただいて申し訳ありません。 大変助かりました。 twck様の全体を書きだせないという情報を受け、SpreadsheetDocument自体をどうにか別に保存できないかなと調べてみたところ、以下がヒットしました。 http://stackoverflow.com/questions/5203150/openxml-spreadsheat-save-as MemoryStreamにファイルのバイト配列を書き込んで、そのMemoryStreamを使ってSpreadsheetDocumentをopenするというやり方です。 結果、意図していたことができました。 参考としてできたコードを追記しておきます。 本当にありがとうございました。
twck

2016/09/07 07:50

あれ~?MemoryStreamを使ってOpenしたらCloseするとき(usingが閉じるとき)に例外が発生したからBufferedStreamに変えたのに、リンク先見てMemoryStreamに戻したら正常に動作するよ!何故だ! とりあえず出来てよかったですね。後で回答をMemoryStreamだけを使ったものに書き直します。
twck

2016/09/07 08:02

回答を変更しました。 BufferedStreamとMemoryStreamを使用した方法から、MemoryStreamだけを使用した方法に変更しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問