c# - 我可以只使用 Office Web Apps Server 吗

标签 c# .net sharepoint ms-office ms-wopi

我想在带有 office 的浏览器中安装一个文档管理系统。我找到了这个解决方案 http://www.edrawsoft.com/officeviewer.php但它使用 office 的客户端副本。

我想使用 Office Web Apps,但我的问题是......我是否需要通过 SharePoint 或其他 Microsoft 产品使用它,或者我是否可以连接一个网站以在浏览器中为我自己的文档系统使用 Office Web Apps

最佳答案

您可以编写自己的实现 WOPI 协议(protocol)的服务器,这将在查看/编辑模式下支持 PPTX/XSLX,仅在查看模式下支持 DOCX/PDF。 WOPI 服务器实现起来非常简单。

要编辑 word 文档,您需要实现 Cobalt 或 FSSHTTP/FSSHTTPB 协议(protocol)。

另外不要忘记许可,Office Web Apps 要求所有用户都拥有有效的 office 许可证。

这是一个示例 .NET WOPI 服务器:

调用它:

http://OFFICEWEBAPPS.HOST/p/PowerPointFrame.aspx?PowerPointView=EditView&access_token=12345&WOPISrc=URLENCODED_URL_OF_THE_WOPI_SERVER

喜欢:http://WOPISERVER.HOST:2000/wopi/files/1.pptx

这将在您的 c:\temp 中打开 1.pptx

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Runtime.Serialization;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using System.IO;
using System.Runtime.Serialization.Json;

namespace WopiServerTutorial
{
    public class WopiServer
    {
        private HttpListener Listener;

        static void Main(string[] args)
        {
            WopiServer s = new WopiServer();
            s.Start();

            Console.WriteLine("A simple wopi webserver. Press a key to quit.");
            Console.ReadKey();

            s.Stop();
        }

        public void Start()
        {
            Listener = new HttpListener();
            Listener.Prefixes.Add(@"http://+:8080/");
            Listener.Start();
            Listener.BeginGetContext(ProcessRequest, Listener);
            Console.WriteLine(@"WopiServer Started");
        }

        public void Stop()
        {
            Listener.Stop();
        }

        private void ProcessRequest(IAsyncResult result)
        {
            HttpListener listener = (HttpListener)result.AsyncState;
            HttpListenerContext context = listener.EndGetContext(result);

            Console.WriteLine(@"Got a " + context.Request.HttpMethod  + " request for URL: " + context.Request.Url.PathAndQuery);
            var stringarr = context.Request.Url.AbsolutePath.Split('/');
            var rootDir = @"C:\\temp\\";

            if (stringarr.Length == 5 && context.Request.HttpMethod.Equals(@"GET"))
            {
                Console.WriteLine(@"Getting content for the file: " + rootDir + stringarr[3]);

                // get file's content
                var file = rootDir + stringarr[3];
                var stream = new FileStream(file, FileMode.Open);
                var fi = new FileInfo(file);

                context.Response.ContentType = @"application/octet-stream";
                context.Response.ContentLength64 = fi.Length;
                stream.CopyTo(context.Response.OutputStream);
                context.Response.Close();
            }
            //else if (stringarr.Length == 5 && context.Request.HttpMethod.Equals(@"POST"))
            //{
            //    // write
            //}
            else if (stringarr.Length == 4 && context.Request.HttpMethod.Equals(@"GET"))
            {
                Console.WriteLine(@"Getting metdata for the file: " + rootDir + stringarr[3]);
                var fi = new FileInfo(rootDir + stringarr[3]);

                CheckFileInfo cfi = new CheckFileInfo();
                cfi.AllowExternalMarketplace = false;
                cfi.BaseFileName = fi.Name;
                cfi.BreadcrumbBrandName = "";
                cfi.BreadcrumbBrandUrl = "";
                cfi.BreadcrumbDocName = "";
                cfi.BreadcrumbDocUrl = "";
                cfi.BreadcrumbFolderName = "";
                cfi.BreadcrumbFolderUrl = "";
                cfi.ClientUrl = "";
                cfi.CloseButtonClosesWindow = false;
                cfi.CloseUrl = "";
                cfi.DisableBrowserCachingOfUserContent = true;
                cfi.DisablePrint = true;
                cfi.DisableTranslation = true;
                cfi.DownloadUrl = "";
                cfi.FileUrl = "";
                cfi.FileSharingUrl = "";
                cfi.HostAuthenticationId = "s-1-5-21-3430578067-4192788304-1690859819-21774";
                cfi.HostEditUrl = "";
                cfi.HostEmbeddedEditUrl = "";
                cfi.HostEmbeddedViewUrl = "";
                cfi.HostName = @"SharePoint";
                cfi.HostNotes = @"HostBIEnabled";
                cfi.HostRestUrl = "";
                cfi.HostViewUrl = "";
                cfi.IrmPolicyDescription = "";
                cfi.IrmPolicyTitle = "";
                cfi.OwnerId = @"4257508bfe174aa28b461536d8b6b648";
                cfi.PresenceProvider = "AD";
                cfi.PresenceUserId = @"S-1-5-21-3430578067-4192788304-1690859819-21774";
                cfi.PrivacyUrl = "";
                cfi.ProtectInClient = false;
                cfi.ReadOnly = false;
                cfi.RestrictedWebViewOnly = false;
                cfi.SHA256 = "";
                cfi.SignoutUrl = "";
                cfi.Size = fi.Length;
                cfi.SupportsCoauth = false;
                cfi.SupportsCobalt = false;
                cfi.SupportsFolders = false;
                cfi.SupportsLocks = true;
                cfi.SupportsScenarioLinks = false;
                cfi.SupportsSecureStore = false;
                cfi.SupportsUpdate = true;
                cfi.TenantId = @"33b62539-8c5e-423c-aa3e-cc2a9fd796f2";
                cfi.TermsOfUseUrl = "";
                cfi.TimeZone = @"+0300#0000-11-00-01T02:00:00:0000#+0000#0000-03-00-02T02:00:00:0000#-0060";
                cfi.UserCanAttend = false;
                cfi.UserCanNotWriteRelative = false;
                cfi.UserCanPresent = false;
                cfi.UserCanWrite = true;
                cfi.UserFriendlyName = "";
                cfi.UserId = "";
                cfi.Version = @"%22%7B59CCD75F%2D0687%2D4F86%2DBBCF%2D059126640640%7D%2C1%22";
                cfi.WebEditingDisabled = false;

                // encode json
                var memoryStream = new MemoryStream();
                var json = new DataContractJsonSerializer(typeof(CheckFileInfo));
                json.WriteObject(memoryStream, cfi);
                memoryStream.Flush();
                memoryStream.Position = 0;
                StreamReader streamReader = new StreamReader(memoryStream);
                var jsonResponse = Encoding.UTF8.GetBytes(streamReader.ReadToEnd());

                context.Response.ContentType = @"application/json";
                context.Response.ContentLength64 = jsonResponse.Length;
                context.Response.OutputStream.Write(jsonResponse, 0, jsonResponse.Length);
                context.Response.Close();
            }
            else
            {
                byte[] buffer = Encoding.UTF8.GetBytes("");
                context.Response.ContentLength64 = buffer.Length;
                context.Response.ContentType = @"application/json";
                context.Response.OutputStream.Write(buffer, 0, buffer.Length);
                context.Response.OutputStream.Close();
            }

            Listener.BeginGetContext(ProcessRequest, Listener);
        }
    }

    [DataContract]
    public class CheckFileInfo
    {
        [DataMember]
        public bool AllowExternalMarketplace { get; set; }
        [DataMember]
        public string BaseFileName { get; set; }
        [DataMember]
        public string BreadcrumbBrandName { get; set; }
        [DataMember]
        public string BreadcrumbBrandUrl { get; set; }
        [DataMember]
        public string BreadcrumbDocName { get; set; }
        [DataMember]
        public string BreadcrumbDocUrl { get; set; }
        [DataMember]
        public string BreadcrumbFolderName { get; set; }
        [DataMember]
        public string BreadcrumbFolderUrl { get; set; }
        [DataMember]
        public string ClientUrl { get; set; }
        [DataMember]
        public bool CloseButtonClosesWindow { get; set; }
        [DataMember]
        public string CloseUrl { get; set; }
        [DataMember]
        public bool DisableBrowserCachingOfUserContent { get; set; }
        [DataMember]
        public bool DisablePrint { get; set; }
        [DataMember]
        public bool DisableTranslation { get; set; }
        [DataMember]
        public string DownloadUrl { get; set; }
        [DataMember]
        public string FileSharingUrl { get; set; }
        [DataMember]
        public string FileUrl { get; set; }
        [DataMember]
        public string HostAuthenticationId { get; set; }
        [DataMember]
        public string HostEditUrl { get; set; }
        [DataMember]
        public string HostEmbeddedEditUrl { get; set; }
        [DataMember]
        public string HostEmbeddedViewUrl { get; set; }
        [DataMember]
        public string HostName { get; set; }
        [DataMember]
        public string HostNotes { get; set; }
        [DataMember]
        public string HostRestUrl { get; set; }
        [DataMember]
        public string HostViewUrl { get; set; }
        [DataMember]
        public string IrmPolicyDescription { get; set; }
        [DataMember]
        public string IrmPolicyTitle { get; set; }
        [DataMember]
        public string OwnerId { get; set; }
        [DataMember]
        public string PresenceProvider { get; set; }
        [DataMember]
        public string PresenceUserId { get; set; }
        [DataMember]
        public string PrivacyUrl { get; set; }
        [DataMember]
        public bool ProtectInClient { get; set; }
        [DataMember]
        public bool ReadOnly { get; set; }
        [DataMember]
        public bool RestrictedWebViewOnly { get; set; }
        [DataMember]
        public string SHA256 { get; set; }
        [DataMember]
        public string SignoutUrl { get; set; }
        [DataMember]
        public long Size { get; set; }
        [DataMember]
        public bool SupportsCoauth { get; set; }
        [DataMember]
        public bool SupportsCobalt { get; set; }
        [DataMember]
        public bool SupportsFolders { get; set; }
        [DataMember]
        public bool SupportsLocks { get; set; }
        [DataMember]
        public bool SupportsScenarioLinks { get; set; }
        [DataMember]
        public bool SupportsSecureStore { get; set; }
        [DataMember]
        public bool SupportsUpdate { get; set; }
        [DataMember]
        public string TenantId { get; set; }
        [DataMember]
        public string TermsOfUseUrl { get; set; }
        [DataMember]
        public string TimeZone { get; set; }
        [DataMember]
        public bool UserCanAttend { get; set; }
        [DataMember]
        public bool UserCanNotWriteRelative { get; set; }
        [DataMember]
        public bool UserCanPresent { get; set; }
        [DataMember]
        public bool UserCanWrite { get; set; }
        [DataMember]
        public string UserFriendlyName { get; set; }
        [DataMember]
        public string UserId { get; set; }
        [DataMember]
        public string Version { get; set; }
        [DataMember]
        public bool WebEditingDisabled { get; set; }
    }
}

2014 年 6 月 13 日更新:

使用 Cobalt API 可以编辑 word 文档,尽管我的实现远非完美。让我们了解基础知识。 FSSHTTP 需要 Shredded Storage,SharePoint 在数据库中实现,但是在查看 Cobalt Assembly 之后我确定了在文件系统上创建一个碎片文件的方法......这是相关代码的片段,第一部分从 c 中获取 word doc :\tmp\test.docx 并在 c:\tmp\filestore 和 c:\tmp\wacupdate 中转换为切碎的 blob

            DisposalEscrow disposal = new DisposalEscrow("temp1");

            CobaltFilePartitionConfig content = new CobaltFilePartitionConfig();
            content.IsNewFile = false;
            content.HostBlobStore = new FileSystemHostBlobStore("C:\\tmp\\filestore\\", "filestore", new FileSystemHostBlobStore.Config(), disposal, true, false);
            content.cellSchemaIsGenericFda = true;
            content.CellStorageConfig = new CellStorageConfig();
            content.Schema = CobaltFilePartition.Schema.ShreddedCobalt;
            content.PartitionId = FilePartitionId.Content;

            CobaltFilePartitionConfig wacupdate = new CobaltFilePartitionConfig();
            wacupdate.IsNewFile = false;
            wacupdate.HostBlobStore = new FileSystemHostBlobStore("C:\\tmp\\wacstore\\", "wacstore", new FileSystemHostBlobStore.Config(), disposal, true, false);
            wacupdate.cellSchemaIsGenericFda = false;
            wacupdate.CellStorageConfig = new CellStorageConfig();
            wacupdate.Schema = CobaltFilePartition.Schema.ShreddedCobalt;
            wacupdate.PartitionId = FilePartitionId.WordWacUpdate;

            Dictionary<FilePartitionId, CobaltFilePartitionConfig> pd = new Dictionary<FilePartitionId, CobaltFilePartitionConfig>();
            pd.Add(FilePartitionId.Content, content);
            pd.Add(FilePartitionId.WordWacUpdate, wacupdate);

    // custom locking store is my implementation of hostlockingstore
            CobaltFile cobaltFile = new CobaltFile(disposal, pd, new CustomHostLockingStore(), null);

            var src = FileAtom.FromExisting("C:\\tmp\\Test.docx", disposal);
            Cobalt.Metrics o1;
            cobaltFile.GetCobaltFilePartition(FilePartitionId.Content).SetStream(RootId.Default.Value, src, out o1);
            cobaltFile.GetCobaltFilePartition(FilePartitionId.Content).GetStream(RootId.Default.Value).Flush();

            cobaltFile.CommitChanges();

现在你有了 cobaltFile,你可以用这个编辑我原来的 wopi 代码:

        }
        else if (context.Request.HttpMethod.Equals(@"POST") && context.Request.Headers["X-WOPI-Override"].Equals("COBALT"))
        {
            Console.WriteLine(@"Got a cobalt request for the file");
            var ms = new MemoryStream();
            context.Request.InputStream.CopyTo(ms);
            AtomFromByteArray atomRequest = new AtomFromByteArray(ms.ToArray());
            RequestBatch requestBatch = new RequestBatch();

            Object ctx;
            ProtocolVersion protocolVersion;

            requestBatch.DeserializeInputFromProtocol(atomRequest, out ctx, out protocolVersion);

            cobaltFile.CobaltEndpoint.ExecuteRequestBatch(requestBatch);
            cobaltFile.CommitChanges();


            var response = requestBatch.SerializeOutputToProtocol(protocolVersion);

            context.Response.Headers.Add("X-WOPI-MachineName", "test");
            context.Response.Headers.Add("X-WOPI-CorellationID", context.Request.Headers["X-WOPI-CorrelationID"]);
            context.Response.Headers.Add("request-id", context.Request.Headers["X-WOPI-CorrelationID"]);
            context.Response.ContentType = @"application/octet-stream";
            context.Response.ContentLength64 = response.Length;
            response.CopyTo(context.Response.OutputStream);
            context.Response.Close();

        }

和定制锁店即将推出。从切碎的文档转换回 word 文档我还没有做过,但我想我知道怎么做

最后的更新 - 完整的解决方案在这里:

https://github.com/thebitllc/WopiBasicEditor

关于c# - 我可以只使用 Office Web Apps Server 吗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17065029/

相关文章:

c# - AutoMapper 将源对象上的单个列表映射到目标对象上的两个列表

c# - 无法为表中的标识列插入显式值

c# - 将 httpwebresponse 写入文件——powershell——简短而甜美(管道)

c# - 如何删除仍在其他程序中打开的临时文件?

.net - 用于异常处理的开源服务器应用程序?

sharepoint - 用于定期将 Google 文档传输到 SharePoint 的 Google 脚本

sharepoint - 隐藏已弃用的共享点网站

c# - 如何重命名 XML 序列化列表 <string> 中使用的子 XML 元素?

.net - Linq 和对象的包装器

c# - 如何修复错误 : The message received from the server could not be parsed