前言

最近换新公司了,一直挺忙的,好久没正儿八经的吹牛逼了!新公司技术部门有定期学习计划,和技术储备计划,这不来了个在线预览文档的活么,作为公司技术储备,需求产生的背景:公司Word、Excel、PDF等文档挺多的,基本上都是展示文件名,然后点击下载到电脑才能查看的这种,不是很方便,那么直接搞成在线预览的可能比较香吧!

正文

本文将是全网最详细的Office Online Server 2016搭建教程,提供服务器版本选择,Windows Server 启动盘制作,Windows服务器搭建,域服务器配置,Windows服务器组网,Office Online Server2016 前置环境搭建、Office Online Server 2016 安装,Wopi服务搭建,以及一些坑的解决!首先需要准备两套服务器,而且都必须是Windows Server 2012 R2版本的服务器,推荐使用下方红框内的ISO镜像,其他版本的安装更新时会报错

实现效果

docx
Office Online Server搭建(全网最详细)-DESTLIVE
xlsx
Office Online Server搭建(全网最详细)-DESTLIVE
ppt
Office Online Server搭建(全网最详细)-DESTLIVE
等其他的格式我这里就不易伊一列出来了!

服务器版本选择

这里版本一定要搞对,不然各种问题,选择windows_server_2012_r2,看到我这里三个不同的Windows Server版本就知道这里版本不对有多少坑了吧,我这里不跟你们说建议用那个版本,你们就用cn_windows_server_2012_r2_vl_with_update_x64_dvd_4051059.iso版本,如果想采坑的自便!
Office Online Server搭建(全网最详细)-DESTLIVE
迅雷下载地址

ed2k://|file|cn_windows_server_2012_r2_vl_with_update_x64_dvd_4051059.iso|4683122688|BD0B95997679F83A4EE2D062865D8E64|/

4个多G,用迅雷下载不慢亲测5分钟!

Windows Server 系统启动盘制作

镜像下载下来后,我们开始制作Windows Server 2016的启动盘制作

启动盘制作工具我们选用U深度装机U深度装机官网

插入U盘

启动U深度装机

选择ISO模式
Office Online Server搭建(全网最详细)-DESTLIVE
选择镜像
Office Online Server搭建(全网最详细)-DESTLIVE
注意镜像千万别搞错了,是这个 cn_windows_server_2012_r2_x64_dvd_2707961.iso

开始制作
Office Online Server搭建(全网最详细)-DESTLIVE

Office Online Server搭建(全网最详细)-DESTLIVE
Office Online Server搭建(全网最详细)-DESTLIVE
注意这里选择否,详细问题请见我的往期文章有提到这里为什么不选择是!Linux系统安装

写入镜像
Office Online Server搭建(全网最详细)-DESTLIVE
启动盘最后大小
Office Online Server搭建(全网最详细)-DESTLIVE

Windows Server 系统安装

这个每个电脑主板不同进入方式和安装方式都不同,我这里就以戴尔、麦本本举例吧!

戴尔
开机后长按F12,进入主板,选择USB模式,然后就会进入Windows Server 系统安装引导界面,然后傻瓜式操作点点点就行!

麦本本
开机后长按F2,进入主板,这里和戴尔的有点不同,需要切换启动盘顺序,详细查阅官网吧!这个我怕用文字描述吓到你们。然后就是傻瓜式操作了!

搭建域控服务器

打开服务器管理器,添加角色和功能;
Office Online Server搭建(全网最详细)-DESTLIVE
出现“添加角色和功能”界面,下一步 。
Office Online Server搭建(全网最详细)-DESTLIVE
根据提示操作,下一步。
Office Online Server搭建(全网最详细)-DESTLIVE
根据提示操作,下一步。
Office Online Server搭建(全网最详细)-DESTLIVE
选择添加AD域服务(Active Directory 域服务),同时添加所需功能。
Office Online Server搭建(全网最详细)-DESTLIVE
根据提示操作,下一步
Office Online Server搭建(全网最详细)-DESTLIVE
安装完成 。
Office Online Server搭建(全网最详细)-DESTLIVE
配置域
点击服务器管理器左侧“AD DS” 、点击黄色提示部分中的更多。
Office Online Server搭建(全网最详细)-DESTLIVE
点击“将此服务器升级为域控制器” 。
Office Online Server搭建(全网最详细)-DESTLIVE
进入AD域服务器配置向导,选择 “添加新林” ,输入域,点击下一步。
Office Online Server搭建(全网最详细)-DESTLIVE

域填写这个,contoso.com不要问我为什么,照着弄就行!

填写密码,下一步 。
Office Online Server搭建(全网最详细)-DESTLIVE
注意一下,这里两台服务器密码最好保持一致,服务器时间要校准
提示DNS无法创建,不用管,继续下一步 。
Office Online Server搭建(全网最详细)-DESTLIVE
安装路径,默认,下一步 。
Office Online Server搭建(全网最详细)-DESTLIVE
查看选项,默认,下一步 。

Office Online Server搭建(全网最详细)-DESTLIVE
点击安装 。此过程可能会报错,提示登陆用户没有设置密码,去设置给当前登陆用户(Administrator)设置一个密码,然后回来点击 “重新运行先决条件检查” 即可!
Office Online Server搭建(全网最详细)-DESTLIVE
安装完成,会提示注销重启 (此过程比较漫长,耐心等待)。
Office Online Server搭建(全网最详细)-DESTLIVE

Windows服务器组网

打开转换服务器,修改转换服务器的NDS,指向域控服务器(刚才的服务器ip地址)。
Office Online Server搭建(全网最详细)-DESTLIVE
在计算机属性中,修改计算机名称(随意),并添加到域控服务器(域为“添加新林”时设置的域名),确定后提示注销重启服务器。

Office Online Server搭建(全网最详细)-DESTLIVE
打开域控服务器,点击管理 > 添加服务器,输入转换服务器修改后的计算机名称,立即查找。将搜索到的服务器双击添加到右边,点击确定。
Office Online Server搭建(全网最详细)-DESTLIVE
Office Online Server搭建(全网最详细)-DESTLIVE
在域控服务器中,所有服务器显示两台服务器,并都是联机状态则表示成功 。
Office Online Server搭建(全网最详细)-DESTLIVE

Office Online Server2016 前置环境搭建

Office Online Server 官网
知道你们不喜欢看官网,那就为难一下我搬运一下官网吧!不过微软的门户做的挺好的,尤其是下载软件的通道,但是他们注册的渠道是真TM恶心,机器人验证的时候简直就是智障,不信你们可以体验一下!

以管理员身份打开 Microsoft PowerShell 提示符,然后运行此命令示例来安装必需的角色和服务。

Add-WindowsFeature Web-Server,Web-Mgmt-Tools,Web-Mgmt-Console,Web-WebServer,Web-Common-Http,Web-Default-Doc,Web-Static-Content,Web-Performance,Web-Stat-Compression,Web-Dyn-Compression,Web-Security,Web-Filtering,Web-Windows-Auth,Web-App-Dev,Web-Net-Ext45,Web-Asp-Net45,Web-ISAPI-Ext,Web-ISAPI-Filter,Web-Includes,InkandHandwritingServices,NET-Framework-Features,NET-Framework-Core,NET-HTTP-Activation,NET-Non-HTTP-Activ,NET-WCF-HTTP-Activation45,Windows-Identity-Foundation,Server-Media-Foundation

Office Online Server搭建(全网最详细)-DESTLIVE
Office Online Server搭建(全网最详细)-DESTLIVE
安装完成后重启。

NET Framework 4.5.2
Office Online Server搭建(全网最详细)-DESTLIVE
点击下载即可,后面几个环节都是!顺序不能错哦!

Visual C++ Redistributable Packages for Visual Studio 2013

Visual C++ Redistributable for Visual Studio 2015

Microsoft.IdentityModel.Extention.dll

Office Online Server搭建(全网最详细)-DESTLIVE
Office Online Server搭建(全网最详细)-DESTLIVE
Office Online Server搭建(全网最详细)-DESTLIVE
Office Online Server搭建(全网最详细)-DESTLIVE

Office Online Server 2016 安装

Office Online Server 2016这个安装包还挺难找的,如果从官网上下在,那就会碰到那个智障机器人检验,注册入口我放着,有想体验的自己可以试试!访问批量许可服务中心

百度网盘下载密码: guc8Office Online Server 2016
Office Online Server搭建(全网最详细)-DESTLIVE
解压
Office Online Server搭建(全网最详细)-DESTLIVE
再解压n-moos16.rar得到n-moos16.iso
Office Online Server搭建(全网最详细)-DESTLIVE

下面进入主题了,安装office online。
双击下载好的office online 安装包,进行安装。
Office Online Server搭建(全网最详细)-DESTLIVE
安装成功。
Office Online Server搭建(全网最详细)-DESTLIVE
安装语言包。

Office Online Server搭建(全网最详细)-DESTLIVE
安装成功。
Office Online Server搭建(全网最详细)-DESTLIVE
启动office Online 服务器场

打开安装office Online 的主机,打开powerShell。

Import-Module OfficeWebApps

部署服务器场:

New-OfficeWebAppsFarm -InternalURL “http://officeserver.contoso.com” -ExternalUrl “http://192.168.1.24-AllowHttpEditingEnabled

-InternalURL:内网浏览地址,http://xx.domin.com 其中 xx表示计算机名 domin.com 表示域名 也可以设置为对应的IP地址
-ExternalURL:外网浏览地址
-AllowHttp: 允许80端口访问
-OpenFromUrlEnabled:允许通过url方式进行预览
-CacheLocation: 缓存文件存放路径 默认是C:\ProgramData\Microsoft\OfficeWebApps\Working\d
-CacheSizeInGB: 最大缓存文件大小 单位GB 默认为15GB

Office Online Server搭建(全网最详细)-DESTLIVE

关闭两台服务器防火墙
访问http:// 192.168.1.24/hosting/discovery 测试是否启动
Office Online Server搭建(全网最详细)-DESTLIVE
若http:// 192.168.1.24/hosting/discovery 能登录,http://192.168.1.24/op/generate.aspx显示“服务器错误”,控制台输入Set-OfficeWebAppsFarm -OpenFromUrlEnabled:$true即可访问成功

访问http://officeserver.contoso.com/op/generate.aspx测试 http://officeserver.contoso.com为officeserver地址
Office Online Server搭建(全网最详细)-DESTLIVE
ok

Wopi服务搭建

上代码吧,做点程序员该干的活吧!

使用SpringBoot脚手架创建一个web工程

FileInfo

package com.tao.wopihost.entity;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.io.Serializable;

/**
 * @author TAO
 * @description: TODO
 * @date 2021/6/26 20:48
 */
public class FileInfo implements Serializable {

    /**
     * 必须字段
     */
    // 包含扩展的文件名
    @JsonProperty("BaseFileName")
    private String baseFileName;
    // 文件大小(单位:byte)
    @JsonProperty("Size")
    private long size;
    // 唯一标识文件所有者
    @JsonProperty("OwnerId")
    private String ownerId;
    // 文件版本号,文件如果被编辑,版本号也要跟着改变
    @JsonProperty("Version")
    private long version;
    // SHA-2 256位散列编码值(获取该文件的文件流->SHA256计算文件流Hash值->将Hash值转换为Base64String)
    @JsonProperty("SHA256")
    private String sha256;

    /**
     * 编辑时可能用到的字段
     */
    // 是否允许连接到文件中引用的外部服务(例如,可嵌入javascript应用程序)
    @JsonProperty("AllowExternalMarketplace")
    private boolean allowExternalMarketplace = true;
    // 是否有权限修改
    @JsonProperty("UserCanWrite")
    private boolean userCanWrite = true;
    // 是否支持更新
    @JsonProperty("SupportsUpdate")
    private boolean supportsUpdate = true;
    // 是否支持锁定
    @JsonProperty("SupportsLocks")
    private boolean supportsLocks = true;

    /**
     * 其他字段
     */
    // 是否支持使用WOPI客户端创建新文件
    private boolean SupportsFileCreation = true;
    // 是否支持用户可以通过受限制的URL以有限的方式对文件进行操作的方案
    private boolean SupportsScenarioLinks = true;

    public String getBaseFileName() {
        return baseFileName;
    }

    public void setBaseFileName(String baseFileName) {
        this.baseFileName = baseFileName;
    }

    public long getSize() {
        return size;
    }

    public void setSize(long size) {
        this.size = size;
    }

    public String getOwnerId() {
        return ownerId;
    }

    public void setOwnerId(String ownerId) {
        this.ownerId = ownerId;
    }

    public long getVersion() {
        return version;
    }

    public void setVersion(long version) {
        this.version = version;
    }

    public String getSha256() {
        return sha256;
    }

    public void setSha256(String sha256) {
        this.sha256 = sha256;
    }

    public boolean isAllowExternalMarketplace() {
        return allowExternalMarketplace;
    }

    public void setAllowExternalMarketplace(boolean allowExternalMarketplace) {
        this.allowExternalMarketplace = allowExternalMarketplace;
    }

    public boolean isUserCanWrite() {
        return userCanWrite;
    }

    public void setUserCanWrite(boolean userCanWrite) {
        this.userCanWrite = userCanWrite;
    }

    public boolean isSupportsUpdate() {
        return supportsUpdate;
    }

    public void setSupportsUpdate(boolean supportsUpdate) {
        this.supportsUpdate = supportsUpdate;
    }

    public boolean isSupportsLocks() {
        return supportsLocks;
    }

    public void setSupportsLocks(boolean supportsLocks) {
        this.supportsLocks = supportsLocks;
    }

    public boolean isSupportsFileCreation() {
        return SupportsFileCreation;
    }

    public void setSupportsFileCreation(boolean supportsFileCreation) {
        SupportsFileCreation = supportsFileCreation;
    }

    public boolean isSupportsScenarioLinks() {
        return SupportsScenarioLinks;
    }

    public void setSupportsScenarioLinks(boolean supportsScenarioLinks) {
        SupportsScenarioLinks = supportsScenarioLinks;
    }
}

注意属性名称大写转一下!

WopiHostContrller

package com.tao.wopihost.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tao.wopihost.entity.FileInfo;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;


/**
 * @author TAO
 * @description: WOPI HOST
 * @date 2021/6/26 20:48
 */
@RestController
@RequestMapping(value = "/wopi")
public class WopiHostContrller {

    @Value("${file.path}")
    private String filePath;

    /**
     * 获取文件流
     *
     * @param name
     * @param response
     */
    @GetMapping("/files/{name}/contents")
    public void getFile(@PathVariable(name = "name") String name, HttpServletResponse response) {
        System.out.println("GET获取文件啦!!!!");
        InputStream fis = null;
        OutputStream toClient = null;
        try {
            // 文件的路径
            String path = filePath + name;
            File file = new File(path);
            // 取得文件名
            String filename = file.getName();
            // 以流的形式下载文件
            fis = new BufferedInputStream(new FileInputStream(path));
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            // 清空response
            response.reset();

            // 设置response的Header
            response.addHeader("Content-Disposition", "attachment;filename=" +
                    new String(filename.getBytes("utf-8"), "ISO-8859-1"));
            response.addHeader("Content-Length", "" + file.length());
            toClient = new BufferedOutputStream(response.getOutputStream());
            response.setContentType("application/octet-stream");
            toClient.write(buffer);
            toClient.flush();
            System.out.println("GET获取文件Contents结束!!!!");

        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                fis.close();
                toClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 保存更新文件
     *
     * @param name
     * @param content
     */
    @PostMapping("/files/{name}/contents")
    public void postFile(@PathVariable(name = "name") String name, @RequestBody byte[] content) {
        System.out.println("POST获取文件Contents啦!!!!");
        // 文件的路径
        String path = filePath + name;
        File file = new File(path);
        FileOutputStream fop = null;
        try {
            if (!file.exists()) {
                file.createNewFile();//构建文件
            }
            fop = new FileOutputStream(file);
            fop.write(content);
            fop.flush();
            System.out.println("POST获取文件结束!!!!");

            System.out.println("------------ save file ------------ ");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fop.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取文件信息
     *
     * @param request
     * @param response
     * @return
     * @throws UnsupportedEncodingException
     */
    @GetMapping("/files/{name}")
    public void getFileInfo(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("获取文件啦!!!!");
        String uri = request.getRequestURI();
        FileInfo info = new FileInfo();
        try {
            // 获取文件名, 防止中文文件名乱码
            String fileName = URLDecoder.decode(uri.substring(uri.indexOf("wopi/files/") + 11, uri.length()), "UTF-8");
            if (fileName != null && fileName.length() > 0) {
                System.out.println("文件不为空啊");
                File file = new File(filePath + fileName);
                if (file.exists()) {
                    // 取得文件名
                    info.setBaseFileName(file.getName());
                    info.setSize(file.length());
                    info.setOwnerId("admin");
                    info.setVersion(file.lastModified());
                    info.setSha256(getHash256(file));
                    info.setAllowExternalMarketplace(true);
                    info.setUserCanWrite(true);
                    info.setSupportsUpdate(true);
                    info.setSupportsLocks(true);
                }
            }

            ObjectMapper mapper = new ObjectMapper();
            String Json = mapper.writeValueAsString(info);

            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(Json);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取文件的SHA-256值
     *
     * @param file
     * @return
     */
    public static String getHash256(File file) {
        String value = "";
        // 获取hash值
        try {
            byte[] buffer = new byte[1024];
            int numRead;
            InputStream fis = new FileInputStream(file);
            //如果想使用SHA-1或SHA-256,则传入SHA-1,SHA-256
            MessageDigest complete = MessageDigest.getInstance("SHA-256");
            do {
                //从文件读到buffer
                numRead = fis.read(buffer);
                if (numRead > 0) {
                    //用读到的字节进行MD5的计算,第二个参数是偏移量
                    complete.update(buffer, 0, numRead);
                }
            } while (numRead != -1);

            fis.close();
            value = new String(Base64.encodeBase64(complete.digest()));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return value;
    }

}

application.yml

server:
  port: 80


file:
  path: E:\file\

path是域服务器上文件存放位置
打包

Office Online Server搭建(全网最详细)-DESTLIVE
放到域服务器上运行!
Office Online Server搭建(全网最详细)-DESTLIVE
创建测试文档
Office Online Server搭建(全网最详细)-DESTLIVE
注意这里两台服务器需要互相能ping通,

文档地址配置
1.由于微软这款软件对IP有访问限制,所以需将IP转化为域名进行访问,所以需要进行配置,来让软件自动进行域名转化为IP,具体路径如下

Office Online Server搭建(全网最详细)-DESTLIVE

192.168.1.24 officeserver.contoso.com
192.168.1.16 contoso.com

完整性测试

docx
http://officeserver.contoso.com/op/view.aspx?src=http://contoso.com/wopi/files/test1.docx/contents
Office Online Server搭建(全网最详细)-DESTLIVE
xlsx
http://officeserver.contoso.com/op/view.aspx?src=http://contoso.com/wopi/files/test3.xlsx/contents
Office Online Server搭建(全网最详细)-DESTLIVE
ppt
http://officeserver.contoso.com/op/view.aspx?src=http://contoso.com/wopi/files/111.ppt/contentsOffice Online Server搭建(全网最详细)-DESTLIVE

搞定,睡了睡了。