public HttpResponseMessage DownloadDocument([FromUri] DownloadDocumentRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            if ((request.DocumentId == null && request.Uri == null) || (request.DocumentId != null && request.Uri != null))
            {
                throw new InvalidOperationException("DocumentId or Uri must not be null, but not both");
            }

            var cache = ServiceHelper.Cache;
            DocumentCacheInfo cacheInfo;

            if (request.Uri != null)
            {
                cacheInfo = DocumentFactory.GetDocumentCacheInfo(cache, request.Uri);
            }
            else
            {
                cacheInfo = DocumentFactory.GetDocumentCacheInfo(cache, request.DocumentId);
            }
            DocumentHelper.CheckCacheInfo(cacheInfo);

            var documentMimeType  = cacheInfo.MimeType;
            var documentExtension = RasterCodecs.GetMimeTypeExtension(documentMimeType);

            if (string.IsNullOrEmpty(documentExtension))
            {
                documentExtension = "data";
            }

            var name = "download";

            if (!string.IsNullOrEmpty(cacheInfo.Name))
            {
                name = ServiceHelper.RemoveExtension(cacheInfo.Name, documentExtension);
            }

            var documentName    = string.Format("{0}.{1}", name, documentExtension);
            var annotationsName = string.Format("{0}_ann.xml", name);

            var responseFileName    = documentName;
            var responseContentType = documentMimeType;

            bool zipAnnotations = request.IncludeAnnotations && cacheInfo.HasAnnotations;

            if (zipAnnotations)
            {
                // We will create a ZIP file
                responseFileName    = string.Format("{0}.zip", name);
                responseContentType = MediaTypeNames.Application.Zip;
            }

            var documentId = cacheInfo.DocumentId;

            Action <Stream, HttpContent, TransportContext> write = (outputStream, content, context) =>
            {
                if (zipAnnotations)
                {
                    // We must create a new memory stream because Package.Open tries to request position
                    using (var zipStream = new MemoryStream())
                    {
                        using (var package = Package.Open(zipStream, FileMode.CreateNew))
                        {
                            var    documentPart   = package.CreatePart(new Uri(string.Format("/{0}", documentName), UriKind.Relative), documentMimeType);
                            Stream documentStream = documentPart.GetStream();
                            DocumentFactory.DownloadDocument(cache, documentId, 0, -1, documentStream);
                            documentStream.Close();

                            var    annotationsPart   = package.CreatePart(new Uri(string.Format("/{0}", annotationsName), UriKind.Relative), "text/xml");
                            Stream annotationsStream = annotationsPart.GetStream();
                            DocumentFactory.DownloadAnnotations(cache, documentId, 0, -1, annotationsStream);
                            annotationsStream.Close();
                        }

                        zipStream.Position = 0;
                        ServiceHelper.CopyStream(zipStream, outputStream);
                    }
                }
                else
                {
                    // Just the document
                    DocumentFactory.DownloadDocument(cache, documentId, 0, -1, outputStream);
                }
                outputStream.Close();
            };

            var response = new HttpResponseMessage(HttpStatusCode.OK);

            // For "Save to Google Drive" access, we must have the appropriate CORS headers.
            // See https://developers.google.com/drive/v3/web/savetodrive
            response.Headers.Remove("Access-Control-Allow-Headers");
            response.Headers.Add("Access-Control-Allow-Headers", "Range");
            response.Headers.Remove("Access-Control-Expose-Headers");
            response.Headers.Add("Access-Control-Expose-Headers", "Cache-Control, Content-Encoding, Content-Range");

            response.Content = new PushStreamContent(write, new MediaTypeHeaderValue(responseContentType));

            ServiceHelper.SetResponseViewFileName(response, responseFileName, responseContentType);

            return(response);
        }
        public HttpResponseMessage DownloadAnnotations([FromUri] DownloadAnnotationsRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            if ((request.DocumentId == null && request.Uri == null) || (request.DocumentId != null && request.Uri != null))
            {
                throw new InvalidOperationException("DocumentId or Uri must not be null, but not both");
            }

            var cache = ServiceHelper.Cache;
            DocumentCacheInfo cacheInfo;

            if (request.Uri != null)
            {
                cacheInfo = DocumentFactory.GetDocumentCacheInfo(cache, request.Uri);
            }
            else
            {
                cacheInfo = DocumentFactory.GetDocumentCacheInfo(cache, request.DocumentId);
            }
            DocumentHelper.CheckCacheInfo(cacheInfo);

            var name = "download";

            if (!string.IsNullOrEmpty(cacheInfo.Name))
            {
                var documentMimeType  = cacheInfo.MimeType;
                var documentExtension = RasterCodecs.GetMimeTypeExtension(documentMimeType);
                name = ServiceHelper.RemoveExtension(cacheInfo.Name, documentExtension);
            }

            var annotationsName = string.Format("{0}_ann.xml", name);

            var responseFileName    = annotationsName;
            var responseContentType = "application/xml";

            var documentId = cacheInfo.DocumentId;

            Action <Stream, HttpContent, TransportContext> write = (outputStream, content, context) =>
            {
                DocumentFactory.DownloadAnnotations(cache, documentId, 0, -1, outputStream);
                outputStream.Close();
            };

            var response = new HttpResponseMessage(HttpStatusCode.OK);

            // For "Save to Google Drive" access, we must have the appropriate CORS headers.
            // See https://developers.google.com/drive/v3/web/savetodrive
            response.Headers.Remove("Access-Control-Allow-Headers");
            response.Headers.Add("Access-Control-Allow-Headers", "Range");
            response.Headers.Remove("Access-Control-Expose-Headers");
            response.Headers.Add("Access-Control-Expose-Headers", "Cache-Control, Content-Encoding, Content-Range");

            response.Content = new PushStreamContent(write, new MediaTypeHeaderValue(responseContentType));

            ServiceHelper.SetResponseViewFileName(response, responseFileName, responseContentType);

            return(response);
        }