[Community Ring follow-up 1] Office SharePoint Server で Office 文書を自動作成して登録するデモ
こんにちは。
5日(日)の Community Ring は、休日にも関わらず多くの方にご参加いただきありがとうございます。また技術の輪を共に広げましょう。
ところでお約束のデモのソースコードを掲載します。
まずは、Office Developer Center に掲載されている「ある工作機械メーカーA社」のアプリケーションを実装しよう!と題して大失敗した最初のデモについて、本ブログに 添付 しておきました。(デモで使用した Word 文書のテンプレートも添付しました。)
この Word 文書 (ドキュメント) は、お見せした CustomXml の xml データと表示データをバインドしていて、このCustomXml の xml データはデモでお見せした InfoPath のフォームで作成されています。
動作はデモ(のリベンジ戦)で見て頂いた通りですが、あまりにトラブルが多かったので説明が尻切れトンボになってしまいましたので、以下に補足します。
開発方法ですが、ワークフロー自体は、ちゃんと作ろうと思うと、過去の記事 に記載した通りやや難しいのですが、このデモでは、以下の通り、至って簡単なはずでした、、、(四苦八苦してましたが)
- VS でワークフロープロジェクトを新規作成
- WindowsBase の参照を追加し、以下の using を追加
using System.IO;
using System.IO.Packaging;
- onWorkflowActivated ハンドラとして以下のコードを実装
workflowId = workflowProperties.WorkflowId;
- Codeアクティビティの追加とハンドラの実装 (コードは下記を参照)
- sn キーを挿入して (共有アセンブリとして) ビルド
- feature.xml をスニペット挿入して内容をプロジェクトにあわせ変更
- wrokflow.xml をスニペット挿入して内容をプロジェクトにあわせ変更
(この際、アセンブリの PublicKeyToken を sn -T コマンドなどで取得する)
- Install.bat の内容をプロジェクトにあわせ変更
- Install.bat を実行
(以降はデモでご覧頂いた通りです)
また、この処理は、デモ用に作成されたものです (not complete)。
以下に注意してください。
- Open XML によるファイルの読み書きが必要ですが、SharePoint ではデフォルトで設定されるアプリケーションプールは、Network Services の ID で実行されるため、(CASのエラーではなく) プラットフォーム(OS)のアクセス権 (security) のエラーが発生します。このため、実行の前に、あらかじめドライブに権限をあたえるなどして設定しておく必要があります。さらに、ローカルファイルへ書き込むため、排他制御 (exclusive lock) のいっさいを実施していません。
実際の開発では、docx のテンプレートをアセンブリの埋め込まれたリソース (embedded resource) として作成すると良いでしょう。
- ASP.NET、とその上で管理されているワークフローからの実行のため、リソース管理を GC に頼ったコードは禁物です。
特に、Open した Package はちゃんと Close しないと握ったままとなり、その後の読み書きができなくなります。(例外で抜ける際もちゃんと管理してください。)
実際の開発では、using を使用して、処理の最後に、確実に破棄されるコードとしておきましょう。
またソースの内容について説明が不充分でしたので、以下に補足します。
Uri documentUri = null;
Uri customxmlUri = null;
PackagePart documentPart = null;
PackagePart customxmlPart = null;
/*****
Package は確実に Close されるよう、using を使います。
(This is for demo only. Not embedded resource)
*****/
Open the docx container as a System.IO.Packaging.Package.
using (Package docPackage = Package.Open(@"C:\Demo\create_order.docx", FileMode.Open, FileAccess.ReadWrite))
{
/*****
アクセスする際は、常に、リレーションシップUri を取得して、
リレーションシップUri => Part Uri => Part と変換しますので憶えておきましょう。
*****/
foreach (PackageRelationship relationship in docPackage.GetRelationshipsByType(@"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"))
{
documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), relationship.TargetUri);
documentPart = docPackage.GetPart(documentUri);
break; // document root part will be one, so exit soon !
}
/*****
ここも同じですね。
今度は上記で取得したルートを起点に、その下の CustomXml パートを取得しています。
*****/
foreach (PackageRelationship relationship in documentPart.GetRelationshipsByType(@"http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml"))
{
customxmlUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), relationship.TargetUri);
customxmlPart = docPackage.GetPart(customxmlUri);
break;
/*****
要注意: 本番のシステムでは break せずに、System.Xml を使って
Xml をパースし、Namespace でほしい Xml パートを取得してください。
ここではデモのため、CustomXml パートを1つだけと決め付けて処理
しています。
*****/
}
/*****
このワークフローを呼び出したアイテム(InfoPathのXml)のデータ
を SharePoint Server から取得して、取得したバイナリをローカル
のWordのテンプレート(Open XML の zip)のパートに書き込みます。
ここではデモのため排他処理をしていませんが、実際のシステムでは
同時実行に配慮して独自に制御を実装してください。
*****/
SPWeb srcSite = new SPSite(workflowProperties.SiteUrl).OpenWeb();
SPFile srcFile = srcSite.GetFile(workflowProperties.ItemUrl);
byte[] srcBytes = srcFile.OpenBinary();
Stream customxmlStream = customxmlPart.GetStream(FileMode.Create, FileAccess.Write);
for (int i = 0; i < srcBytes.Length; i++)
{
customxmlStream.WriteByte(srcBytes[i]);
}
srcSite.Close();
}
/*****
ローカルに作成した Word 文書を SharePoint Server の該当フォルダにアップします。
*****/
SPWeb dstSite = new SPSite(workflowProperties.SiteUrl).OpenWeb();
SPUser user = dstSite.AllUsers[@"EXAMPLE\DemoUser1"];
dstSite.Files.Add(@"CreateOrder/" + workflowProperties.Item.Name + ".docx", new FileStream(@"C:\Demo\create_order.docx", FileMode.Open),
user, user, DateTime.Now, DateTime.Now);
dstSite.Close();
上記の処理は、普通にコンソールアプリなどでも同様に処理できます。が、その際には、Microsoft.SharePoint のライブラリ (%Program File%\Common Files\Microsoft Shared\web server extensions\12\ISAPI\Microsoft.SharePoint.dll) を参照して取り込んで使用してください。