Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

Package.cs 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using System.Xml;
  8. //using DocumentFormat.OpenXml.Packaging;
  9. namespace System.IO.Packaging
  10. {
  11. public abstract class Package : IDisposable
  12. {
  13. internal const string RelationshipContentType = "application/vnd.openxmlformats-package.relationships+xml";
  14. internal const string RelationshipNamespace = "http://schemas.openxmlformats.org/package/2006/relationships";
  15. internal static readonly Uri RelationshipUri = new Uri("/_rels/.rels", UriKind.Relative);
  16. private PackageProperties packageProperties;
  17. private PackagePartCollection partsCollection;
  18. private Dictionary<string, PackageRelationship> relationships;
  19. private PackageRelationshipCollection relationshipsCollection = new PackageRelationshipCollection();
  20. private Uri Uri = new Uri("/", UriKind.Relative);
  21. private bool Disposed { get; set; }
  22. public FileAccess FileOpenAccess { get; private set; }
  23. public PackageProperties PackageProperties
  24. {
  25. get
  26. {
  27. // PackageProperties are loaded when the relationships are loaded.
  28. // Therefore ensure we've already loaded the relationships.
  29. int count = Relationships.Count;
  30. if (packageProperties == null)
  31. {
  32. packageProperties = new PackagePropertiesPart();
  33. packageProperties.Package = this;
  34. }
  35. return packageProperties;
  36. }
  37. }
  38. private PackagePartCollection PartsCollection
  39. {
  40. get
  41. {
  42. if (partsCollection == null)
  43. {
  44. partsCollection = new PackagePartCollection();
  45. partsCollection.Parts.AddRange(GetPartsCore());
  46. }
  47. return partsCollection;
  48. }
  49. }
  50. private int RelationshipId { get; set; }
  51. private Dictionary<string, PackageRelationship> Relationships
  52. {
  53. get
  54. {
  55. if (relationships == null)
  56. {
  57. LoadRelationships();
  58. }
  59. return relationships;
  60. }
  61. }
  62. private bool Streaming { get; set; }
  63. protected Package(FileAccess openFileAccess)
  64. : this(openFileAccess, false)
  65. {
  66. }
  67. protected Package(FileAccess openFileAccess, bool streaming)
  68. {
  69. FileOpenAccess = openFileAccess;
  70. Streaming = streaming;
  71. }
  72. internal void CheckIsReadOnly()
  73. {
  74. if (FileOpenAccess == FileAccess.Read)
  75. throw new IOException("Operation not valid when package is read-only");
  76. }
  77. public void Close()
  78. {
  79. // FIXME: Ensure that Flush is actually called before dispose
  80. ((IDisposable) this).Dispose();
  81. }
  82. public PackagePart CreatePart(Uri partUri, string contentType)
  83. {
  84. return CreatePart(partUri, contentType, CompressionOption.NotCompressed);
  85. }
  86. public PackagePart CreatePart(Uri partUri, string contentType, CompressionOption compressionOption)
  87. {
  88. CheckIsReadOnly();
  89. Check.PartUri(partUri);
  90. Check.ContentTypeIsValid(contentType);
  91. if (PartExists(partUri))
  92. throw new InvalidOperationException("This partUri is already contained in the package");
  93. PackagePart part = CreatePartCore(partUri, contentType, compressionOption);
  94. PartsCollection.Parts.Add(part);
  95. return part;
  96. }
  97. protected abstract PackagePart CreatePartCore(Uri partUri, string contentType,
  98. CompressionOption compressionOption);
  99. public PackageRelationship CreateRelationship(Uri targetUri, TargetMode targetMode, string relationshipType)
  100. {
  101. return CreateRelationship(targetUri, targetMode, relationshipType, null);
  102. }
  103. public PackageRelationship CreateRelationship(Uri targetUri, TargetMode targetMode, string relationshipType,
  104. string id)
  105. {
  106. return CreateRelationship(targetUri, targetMode, relationshipType, id, false);
  107. }
  108. internal PackageRelationship CreateRelationship(Uri targetUri, TargetMode targetMode, string relationshipType,
  109. string id, bool loading)
  110. {
  111. if (!loading)
  112. CheckIsReadOnly();
  113. Check.TargetUri(targetUri);
  114. if (targetUri.IsAbsoluteUri && targetMode == TargetMode.Internal)
  115. throw new ArgumentException("TargetUri cannot be absolute for an internal relationship");
  116. Check.RelationshipTypeIsValid(relationshipType);
  117. Check.IdIsValid(id);
  118. if (id == null)
  119. id = NextId();
  120. PackageRelationship r = new PackageRelationship(id, this, relationshipType, Uri, targetMode, targetUri);
  121. if (!PartExists(RelationshipUri))
  122. CreatePartCore(RelationshipUri, RelationshipContentType, CompressionOption.NotCompressed).IsRelationship
  123. = true;
  124. Relationships.Add(r.Id, r);
  125. relationshipsCollection.Relationships.Add(r);
  126. if (!loading)
  127. {
  128. using (Stream s = GetPart(RelationshipUri).GetStream())
  129. WriteRelationships(relationships, s);
  130. }
  131. return r;
  132. }
  133. public void DeletePart(Uri partUri)
  134. {
  135. CheckIsReadOnly();
  136. Check.PartUri(partUri);
  137. PackagePart part = GetPart(partUri);
  138. if (part != null)
  139. {
  140. if (part.Package == null)
  141. throw new InvalidOperationException("This part has already been removed");
  142. // FIXME: MS.NET doesn't remove the relationship part
  143. // Instead it throws an exception if you try to use it
  144. if (PartExists(part.RelationshipsPartUri))
  145. GetPart(part.RelationshipsPartUri).Package = null;
  146. part.Package = null;
  147. DeletePartCore(partUri);
  148. PartsCollection.Parts.RemoveAll(p => p.Uri == partUri);
  149. }
  150. }
  151. protected abstract void DeletePartCore(Uri partUri);
  152. public void DeleteRelationship(string id)
  153. {
  154. Check.Id(id);
  155. CheckIsReadOnly();
  156. Relationships.Remove(id);
  157. relationshipsCollection.Relationships.RemoveAll(r => r.Id == id);
  158. if (Relationships.Count > 0)
  159. using (Stream s = GetPart(RelationshipUri).GetStream())
  160. WriteRelationships(relationships, s);
  161. else
  162. DeletePart(RelationshipUri);
  163. }
  164. void IDisposable.Dispose()
  165. {
  166. if (!Disposed)
  167. {
  168. Flush();
  169. Dispose(true);
  170. Disposed = true;
  171. }
  172. }
  173. protected virtual void Dispose(bool disposing)
  174. {
  175. // Nothing here needs to be disposed of
  176. }
  177. private bool flushing = false;
  178. public void Flush()
  179. {
  180. if (FileOpenAccess == FileAccess.Read || flushing)
  181. return;
  182. flushing = true;
  183. // Ensure we've loaded the relationships, parts and properties
  184. int count = Relationships.Count;
  185. if (packageProperties != null)
  186. packageProperties.Flush();
  187. FlushCore();
  188. flushing = false;
  189. }
  190. protected abstract void FlushCore();
  191. public PackagePart GetPart(Uri partUri)
  192. {
  193. Check.PartUri(partUri);
  194. return GetPartCore(partUri);
  195. }
  196. protected abstract PackagePart GetPartCore(Uri partUri);
  197. public PackagePartCollection GetParts()
  198. {
  199. PartsCollection.Parts.Clear();
  200. PartsCollection.Parts.AddRange(GetPartsCore());
  201. return PartsCollection;
  202. }
  203. protected abstract PackagePart[] GetPartsCore();
  204. public PackageRelationship GetRelationship(string id)
  205. {
  206. return Relationships[id];
  207. }
  208. public PackageRelationshipCollection GetRelationships()
  209. {
  210. // Ensure the Relationships dict is instantiated first.
  211. ICollection<PackageRelationship> rels = Relationships.Values;
  212. relationshipsCollection.Relationships.Clear();
  213. relationshipsCollection.Relationships.AddRange(rels);
  214. return relationshipsCollection;
  215. }
  216. public PackageRelationshipCollection GetRelationshipsByType(string relationshipType)
  217. {
  218. PackageRelationshipCollection collection = new PackageRelationshipCollection();
  219. foreach (PackageRelationship r in Relationships.Values)
  220. if (r.RelationshipType == relationshipType)
  221. collection.Relationships.Add(r);
  222. return collection;
  223. }
  224. private void LoadRelationships()
  225. {
  226. relationships = new Dictionary<string, PackageRelationship>();
  227. if (!PartExists(RelationshipUri))
  228. return;
  229. using (Stream stream = GetPart(RelationshipUri).GetStream())
  230. {
  231. XmlDocument doc = new XmlDocument();
  232. doc.Load(stream);
  233. XmlNamespaceManager manager = new XmlNamespaceManager(doc.NameTable);
  234. manager.AddNamespace("rel", RelationshipNamespace);
  235. foreach (XmlNode node in doc.SelectNodes("/rel:Relationships/*", manager))
  236. {
  237. TargetMode mode = TargetMode.Internal;
  238. if (node.Attributes["TargetMode"] != null)
  239. mode = (TargetMode) Enum.Parse(typeof (TargetMode), node.Attributes["TargetMode"].Value);
  240. Uri uri;
  241. try
  242. {
  243. uri = new Uri(node.Attributes["Target"].Value.ToString(), UriKind.Relative);
  244. }
  245. catch
  246. {
  247. uri = new Uri(node.Attributes["Target"].Value.ToString(), UriKind.Absolute);
  248. }
  249. CreateRelationship(uri,
  250. mode,
  251. node.Attributes["Type"].Value.ToString(),
  252. node.Attributes["Id"].Value.ToString(),
  253. true);
  254. }
  255. foreach (PackageRelationship r in relationships.Values)
  256. {
  257. if (r.RelationshipType == PackageProperties.NSPackagePropertiesRelation)
  258. {
  259. PackagePart part = GetPart(PackUriHelper.ResolvePartUri(Uri, r.TargetUri));
  260. packageProperties = new PackagePropertiesPart();
  261. packageProperties.Package = this;
  262. packageProperties.Part = part;
  263. packageProperties.LoadFrom(part.GetStream());
  264. }
  265. }
  266. }
  267. }
  268. private string NextId()
  269. {
  270. while (true)
  271. {
  272. string s = "Re" + RelationshipId.ToString();
  273. if (!Relationships.ContainsKey(s))
  274. return s;
  275. RelationshipId++;
  276. }
  277. }
  278. public static Package Open(Stream stream)
  279. {
  280. return Open(stream, FileMode.Open);
  281. }
  282. public static Package Open(string path)
  283. {
  284. return Open(path, FileMode.OpenOrCreate);
  285. }
  286. public static Package Open(Stream stream, FileMode packageMode)
  287. {
  288. FileAccess access = packageMode == FileMode.Open ? FileAccess.Read : FileAccess.ReadWrite;
  289. return Open(stream, packageMode, access);
  290. }
  291. public static Package Open(string path, FileMode packageMode)
  292. {
  293. return Open(path, packageMode, FileAccess.ReadWrite);
  294. }
  295. public static Package Open(Stream stream, FileMode packageMode, FileAccess packageAccess)
  296. {
  297. return Open(stream, packageMode, packageAccess, false);
  298. }
  299. private static Package Open(Stream stream, FileMode packageMode, FileAccess packageAccess, bool ownsStream)
  300. {
  301. return OpenCore(stream, packageMode, packageAccess, ownsStream);
  302. }
  303. public static Package Open(string path, FileMode packageMode, FileAccess packageAccess)
  304. {
  305. return Open(path, packageMode, packageAccess, FileShare.None);
  306. }
  307. public static Package Open(string path, FileMode packageMode, FileAccess packageAccess, FileShare packageShare)
  308. {
  309. if (packageShare != FileShare.Read && packageShare != FileShare.None)
  310. throw new NotSupportedException("FileShare.Read and FileShare.None are the only supported options");
  311. FileInfo info = new FileInfo(path);
  312. // Bug - MS.NET appears to test for FileAccess.ReadWrite, not FileAccess.Write
  313. if (packageAccess != FileAccess.ReadWrite && !info.Exists)
  314. throw new ArgumentException("packageAccess", "Cannot create stream with FileAccess.Read");
  315. if (info.Exists && packageMode == FileMode.OpenOrCreate && info.Length == 0)
  316. throw new FileFormatException("Stream length cannot be zero with FileMode.Open");
  317. Stream s = File.Open(path, packageMode, packageAccess, packageShare);
  318. return Open(s, packageMode, packageAccess, true);
  319. }
  320. private static Package OpenCore(Stream stream, FileMode packageMode, FileAccess packageAccess, bool ownsStream)
  321. {
  322. if ((packageAccess & FileAccess.Read) == FileAccess.Read && !stream.CanRead)
  323. throw new IOException("Stream does not support reading");
  324. if ((packageAccess & FileAccess.Write) == FileAccess.Write && !stream.CanWrite)
  325. throw new IOException("Stream does not support reading");
  326. if (!stream.CanSeek)
  327. throw new ArgumentException("stream", "Stream must support seeking");
  328. if (packageMode == FileMode.Open && stream.Length == 0)
  329. throw new FileFormatException("Stream length cannot be zero with FileMode.Open");
  330. if (packageMode == FileMode.CreateNew && stream.Length > 0)
  331. throw new IOException("Cannot use CreateNew when stream contains data");
  332. if (packageMode == FileMode.Append || packageMode == FileMode.Truncate)
  333. {
  334. if (stream.CanWrite)
  335. throw new NotSupportedException(string.Format("PackageMode.{0} is not supported", packageMode));
  336. else
  337. throw new IOException(string.Format("PackageMode.{0} is not supported", packageMode));
  338. }
  339. return new ZipPackage(packageAccess, ownsStream, stream);
  340. }
  341. public virtual bool PartExists(Uri partUri)
  342. {
  343. return GetPart(partUri) != null;
  344. }
  345. public bool RelationshipExists(string id)
  346. {
  347. return Relationships.ContainsKey(id);
  348. }
  349. internal static void WriteRelationships(Dictionary<string, PackageRelationship> relationships, Stream stream)
  350. {
  351. XmlDocument doc = new XmlDocument();
  352. XmlNamespaceManager manager = new XmlNamespaceManager(doc.NameTable);
  353. manager.AddNamespace("rel", RelationshipNamespace);
  354. doc.AppendChild(doc.CreateNode(XmlNodeType.XmlDeclaration, "", ""));
  355. XmlNode root = doc.CreateNode(XmlNodeType.Element, "Relationships", RelationshipNamespace);
  356. doc.AppendChild(root);
  357. foreach (PackageRelationship relationship in relationships.Values)
  358. {
  359. XmlNode node = doc.CreateNode(XmlNodeType.Element, "Relationship", RelationshipNamespace);
  360. XmlAttribute idAtt = doc.CreateAttribute("Id");
  361. idAtt.Value = relationship.Id;
  362. node.Attributes.Append(idAtt);
  363. XmlAttribute targetAtt = doc.CreateAttribute("Target");
  364. targetAtt.Value = relationship.TargetUri.ToString();
  365. node.Attributes.Append(targetAtt);
  366. if (relationship.TargetMode != TargetMode.Internal)
  367. {
  368. XmlAttribute modeAtt = doc.CreateAttribute("TargetMode");
  369. modeAtt.Value = relationship.TargetMode.ToString();
  370. node.Attributes.Append(modeAtt);
  371. }
  372. XmlAttribute typeAtt = doc.CreateAttribute("Type");
  373. typeAtt.Value = relationship.RelationshipType;
  374. node.Attributes.Append(typeAtt);
  375. root.AppendChild(node);
  376. }
  377. using (XmlTextWriter writer = new XmlTextWriter(stream, System.Text.Encoding.UTF8))
  378. doc.WriteTo(writer);
  379. }
  380. }
  381. internal class FileFormatException : Exception
  382. {
  383. public FileFormatException(string message): base(message)
  384. {
  385. }
  386. }
  387. }