Office365开发系列——开发一个全功能的Word Add-In

2016年10月我参加了在北京举行的DevDays Asia 2016 - Office 365应用开发”48小时黑客马拉松“,我开发的一个Word Add-In Demo——WordTemplateHelper获得了二等奖。在会场有幸结识了陈希章老师,在与陈老师的交流中受益良多,得知陈老师在准备一个Office解决方案系列后,我想把这个Demo的开发过程简要介绍给大家,以支持陈老师的无私奉献,也希望更多的开发者参与到Office365的开发中来。

Office相关开发主要可以参考这个地址:https://dev.office.com/getting-started

本篇文章主要介绍其中的Office加载项开发,即Office Add-ins:https://msdn.microsoft.com/ZH-CN/library/office/jj220082.aspx 

一、什么是Office Add-Ins

什么是Office Add-ins呢?在陈老师的上一篇文章中,对整个Office发展历史都进行了梳理,我个人的理解就是,开发者可以在Office提供的平台上,对Office做出一定的扩展以实现各种功能,比如之前录制的宏,写的VBS的脚本,某种意义上都可以看做是Office的Add-ins。当然这只是个人理解,不一定准确。目前的Office Add-Ins只支持Office2013以后的版本,开发方式也和以前的VBS有了很大的区别。

现在的Office Add-ins结构是这样的:

一个Office Add-in其实是一个Web App,可以将其部署在任意位置,它可以在一个Office应用程序中运行。有一个manifest.xml清单文件用来指定该Web App如何来呈现,包括定义Web App 的URL。当Office加载这个Add-in时,实际上是提供了一个浏览器的环境,来运行指定的Web App。也就是说,现在开发一个Office Add-in,其实跟开发网页程序差不多,这对熟悉html+JavaScript+css的前端开发人员是非常容易上手的。微软提供了丰富的JavaScript API来对Office进行操作,能实现什么就取决于开发者的想象力了。

一个Word Add-In的实例:

 

二、Word Template Helper需求分析

我在得知有这个活动时,并没有想好要做什么,一直到坐上赴京的高铁,才慢慢有了一个想法,这个想法也是来自平时的工作需要。在工作中经常要撰写大量的文档,如各种软件需求规格说明书、公函、文书、操作手册等,这些文档都有规定的格式,一般情况下我是将一些已经写好的Word文档保存在一个文件夹里当做模板,下次写这种文档的时候复制一份,删删减减的再改。为何不自己写个程序,将这些具有固定模式的文档作为Word模板呢?虽然Word也有自己的模板,但实际上是非常有限的,并不能完全满足我们的需要。如果这个功能做成一个模板商店,大家可以自由上传、分享各自的模板,也许会方便许多。

Word自带的模板是这样的:

这些通用模板对专业性比较强的工作来说是远远不够的。Word Template Helper的效果是这样的:

主意有了,那么就来看一下如何实现。我参加活动时的项目托管在码云上,为了写这篇文章,我重新梳理了这个小demo,在Github上建了一个项目,并尝试使用最新的.NET Core来实现后台API部分。接下来就跟我一起动手吧。

三、项目架构

首先分析一下该项目的结构。文档的模板数据,如模板标题、属性等,需要保存在数据库里,还需要一个Web API项目提供数据,Office Add-in为一个纯前端项目,使用Angular2框架,采用异步调用Web API的数据,实现搜索、加载模板等功能。插件的UI使用微软提供的Fabric UI。整个项目的技术栈如下所示:

至于文档的实体——Word文档,是以Word格式文件存储还是直接保存在数据库中呢?如果是正式项目的话,当然是保存在云存储中是最合适的,但对于一个sample来说,直接保存在数据库中也未尝不可。因为是参加开发马拉松,怎么快怎么来吧。包括ORM框架也是,只是为了快速实现采用的方式,不是最佳实践。

这个sample的开发环境配置如下:

Windows 10 x64,

VS 2017(请确保安装了Office开发工具)

VS Code

Node.js v7.10.0

NPM v4.2.0

ASP.NET Core 1.1

 

四、Web API开发

VS2017已经正式发布了,我使用最新的.NET Core来实现Web API层。

1.新建项目

新建一个空白解决方案,命名为WordTemplateHelpe,然后在其中添加一个ASP.NET Core项目:

选择Web API:

2.安装EF Core

在nuget管理器中搜索安装一下几个Nuget包:

Microsoft.EntityFrameworkCore.SqlServer:EF Core SQL Server

Microsoft.EntityFrameworkCore.Tools:EF命令行工具

Microsoft.EntityFrameworkCore.Tools.DotNet:EF Core命令行工具

3.建立Models

目前最新的EF都推荐使用Code First模式,即直接写Model,EF框架会自动创建所需的数据库。如果习惯DB First的话,也有一个很好的工具推荐:EntityFramework-Reverse-POCO-Code-First-Generator:https://visualstudiogallery.msdn.microsoft.com/ee4fcff9-0c4c-4179-afd9-7a2fb90f5838

 

可以直接在VS的扩展与更新里下载。这个工具可以很方便的根据数据库生成所需的实体类。

首先添加一个模板类型的枚举:

    /// <summary>/// 类型    /// </summary>public enum TemplateType{        /// <summary>/// Private      
  
/// </summary>[Description("Private")]Private = 0,      
 
/// <summary>/// Public      
 
/// </summary>[Description("Public")]Public = 1,      
 
/// <summary>/// Organization      
  
/// </summary>[Description("Organization")]Organization = 2,}

添加一个模板类:

    public class PrivateTemplateInfo{      
  
///<summary>/// Id    
   
///</summary>public string Id { get; set; }        ///<summary>/// User Id    
    
///</summary>public string UserId { get; set; }        ///<summary>/// Template Id  
     
///</summary>public string TemplateId { get; set; }        ///<summary>/// Create Time      
  
///</summary>public DateTime CreateTime { get; set; }}


因为还需要组织机构模板、用户收藏等几个表,这里就不写了,可参考Github上的示例。

4.创建数据库上下文

有了Model后,需要指定哪些实体包含在数据模型中。添加一个Data文件夹,在其中创建一个名为WordTemplateContext.cs的文件:


    public class WordTemplateContext:DbContext{     

   
public WordTemplateContext(DbContextOptions<WordTemplateContext> options) : base(options){}    
   
public DbSet<WordTemplateInfo> WordTemplateInfoes { get; set; }    

   
public DbSet<UserFavoriteInfo> UserFavoriteInfoes { get; set; }      

 
public DbSet<PrivateTemplateInfo> PrivateTemplateInfoes { get; set; }      

 
public DbSet<OrganizationTemplateInfo> OrganizationTemplateInfoes { get; set; }}

 

这样就为每个实体创建了一个DbSet,对应数据库中的表,实体对应表中的行。

 

5.使用依赖注入注册上下文

ASP.NET Core默认实现了依赖注入。要把刚才建立的WordTemplateContext注册成服务,需要在Startup.cs中添加以下代码:


  public void ConfigureServices(IServiceCollection services){            // Add framework services.services.AddDbContext<WordTemplateContext>(options =>options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));services.AddMvc();}


注意要添加using Microsoft.EntityFrameworkCore;不然会找不到UseSqlServer方法。

数据库连接字符串在appsettings.json中配置:

{  "ConnectionStrings": {    "DefaultConnection": "Server=.;User ID=sa;Password=12QWasZX;Initial Catalog=WordTemplate;"},  "Logging": {    "IncludeScopes": false,    "LogLevel": {      "Default": "Warning"}}
}


这里使用了LocalDb,用于测试。当需要正式部署时,这里需要更改为正式数据库服务器的地址及用户名密码。

6.初始化数据库

下面使用命令行初始化数据库。在Data目录下新建一个DbInitializer类,输入以下方法:

    public static class DbInitializer{        

public static void Initialize(WordTemplateContext context){context.Database.EnsureCreated();            //TODO            context.SaveChanges();}}


确保数据被创建。然后修改Startup.cs文件中的Configure方法:

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, WordTemplateContext context){loggerFactory.AddConsole(Configuration.GetSection("Logging"));loggerFactory.AddDebug();app.UseMvc();DbInitializer.Initialize(context);}

 

7.创建API接口

现在写个Controller看看。在Controller文件夹中添加一个控制器:

这里可以使用依赖注入,将数据库上下文注入进来:

    [Produces("application/json")][Route("api/WordTemplate/[action]")]  
   
public class WordTemplateController : Controller{      

     
private readonly WordTemplateContext _context;            public WordTemplateController(WordTemplateContext context){_context = context;}


我们以一个搜索模板的api为例:


        [HttpGet]     
        
public async Task<ResponseResultInfo<List<WordTemplateInfo>>> SearchWordTemplateList(string keyword){ResponseResultInfo<List<WordTemplateInfo>> respResult = new ResponseResultInfo<List<WordTemplateInfo>>();            try{List<WordTemplateInfo> list = await _context.WordTemplateInfoes.Where(x => x.Type == TemplateType.Public && x.Name.Contains(keyword)).OrderByDescending(x => x.CreateTime).ToListAsync();respResult.IsSuccess = true;respResult.Result = list;                return respResult;}      
         
catch (Exception ex){                //LogHelper.ErrorWriteLine("Something wrong. The exception message::{0}", ex);respResult.IsSuccess = false;respResult.Message = string.Format("Something wrong. The exception message::{0}", ex.Message);      
         
return respResult;}}


命令行转到项目目录,运行以下命令

dotnet run

 

可以使用前端调试利器Postman来测试:

API项目运行的具体地址需要记一下,后面做Add-In的时候要用到。具体代码请参考Github。

8.允许跨域访问

为了支持Add-in能够跨域访问我们的接口,还需要安装以下的库:

然后在Startup.cs的ConfigureServices方法中添加以下代码:

#region 跨域services.AddCors(options =>options.AddPolicy("AllowCrossDomain",builder => builder.WithOrigins().AllowAnyMethod().AllowAnyHeader().AllowAnyOrigin().AllowCredentials()));            #endregion


在需要跨域的WordTemplateController上添加一行:

[EnableCors("AllowCrossDomain ")]

 

这样api就可以支持跨域访问了。

 

五、Word Add-In开发

有了API,就可以开发Add-In部分了。开篇说到,Add-In实际上是一个Web App,通过JavaScript操作Office文档对象,具体到这个项目来说,就是使用异步的js去查询、上传、搜索存在服务器上的模板文件,并动态的对当前Word文档进行操作。

微软在Github上开源了这个JavaScript API:https://github.com/Microsoft/Office-js-docs_zh-cn,相关文档:https://msdn.microsoft.com/zh-cn/library/office/fp142185.aspx

开发步骤可参考:https://github.com/Microsoft/Office-js-docs_zh-cn/blob/master/docs/get-started/create-and-debug-office-add-ins-in-visual-studio.md

下面来开发Add-In部分。

1.新建Add-In项目

在解决方案上点击右键,添加一个Word Web外接程序:

添加完成后,多了两个项目:

其中一个是清单文件,带Web后缀的就是Web App了。

2.设置manifest.xml

清单文件是非常重要的一个文件,描述加载项的所有设置。这个文件是自动生成的,但需要我们手动修改一些地方。好在文件中都有注释,所以修改还比较容易:

最重要的是修改SourceLocation这个节点,这个地址设置的是Web App托管的位置。在Web端开发并部署后,要将这个节点改为正确的位置才能发布。下面这个节点也要改掉。

3.Web App分析

可以先运行一下这个模板试试,直接F5:

点击此处就可以调出这个插件:

 

会自动打开Word并加载这个插件,文档中的文本就是插件插入的。那么是哪里的代码起作用的呢?

打开Home.js文件,找到如下代码:

    function loadSampleData() {      
  
// Run a batch operation against the Word object model.Word.run(function (context) {      
     
// Create a proxy object for the document body.var body = context.document.body;            // Queue a commmand to clear the contents of the body.            body.clear();        
     
// Queue a command to insert text into the end of the Word document body.            body.insertText(                "This is a sample text inserted in the document",Word.InsertLocation.end);    
       
// Synchronize the document state by executing the queued commands, and return a promise to indicate task completion.return context.sync();}).catch(errorHandler);}


这里的Word就是JavaScript API提供的对象,可以方便的对当前文档内容进行操作。这样思路就有了,可以通过JavaScript动态去调用Web API获取查询结果,将查询到的文档内容插入到当前文档中,就实现了最初的目的。同时还可以将当前文档的内容保存为模板上传到服务器上进行分享,一个完整功能的sample已经呼之欲出了。

4.使用Angular

我们使用最新的Angular4来开发前端页面。当然如果使用JQuery的话也可以,但现在已经有点out了不是吗?使用Angular可以快速开发一个MVVM架构的单页面WebApp,非常适合这个需求。

这个demo的部分代码参考微软开源的一个项目:https://github.com/OfficeDev/Office-Add-in-UX-Design-Patterns-Code

Angular上手曲线还是有点陡的,官方给出了Angular CLI工具,可以快速搭建一个Angular应用。首先安装TypeScript:

npm install -g typescript

 

然后安装Angular CLI:https://github.com/angular/angular-cli

npm install -g @angular/cli

 

运行以下命令创建一个Angular项目:

ng new WordTemplateHelperSource

 

然后使用cd WordTemplateHelperSource 命令转到项目目录,运行以下命令:

npm install

 

这个命令会安装ng项目所需的依赖,如果安装不成功,建议切换成淘宝npm镜像进行安装。

使用以下命令运行ng项目:

ng serve

 

可以在Chorme浏览器中浏览http://localhost:4200来查看效果:

注意如果在IE中浏览是不正常的,这个问题我们到最后一节再给出解决办法。

为什么不直接在WordTemplateHelperWeb建呢?因为Angular应用还要进行打包,会在项目目录下生成dist目录,这才是正式要运行的部分。所以等开发完成后,将生成的dist目录内的文件拷到WordTemplateHelperWeb就可以了。

在开发Angular的过程中,推荐使用VS Code,对TypeScript和Angular的支持都非常好。

因为本篇文章不是Angular的开发教程,所以Angular的具体知识这里就不展开详述了,感兴趣的话可以自行下载Github代码运行即可。

5.添加操作Word文件的service

为了操作Word文件,我们需要将其封装成服务。使用以下命令添加一个service:

ng g service services\word-document\WordDocument

 

这样会在app目录中的相应路径中生成一个名为WordDocumentService的服务。与此类似,生成其他的几个service。其中主要的几个方法如下:

查询搜索的方法:

/*** search* * @param {string} keyword* @returns {Promise<ResponseResultInfo<Array<WordTemplateInfo>>>}* * @memberOf WordTemplateApiService     */searchWordTemplateList(keyword: string): Promise<ResponseResultInfo<Array<WordTemplateInfo>>> {let url = `${AppGlobal.getInstance().server}/SearchWordTemplateList?keyword=${keyword}`;let promise = this.httpService.get4Json<ResponseResultInfo<Array<WordTemplateInfo>>>(url); 
     
return promise;}


这样可以得到服务器上存储的文档模板,实际是以Ooxml格式保存的string。

对于这个sample来说,使用Office JavaScript API并没有太难的东西,主要用到了两个方法:getOoxml()和insertOoxml(),前者可以读取当前word文档的Ooxml格式,后者可以设置当前word文档的Ooxml格式。Ooxml就是Office2007之后版本使用的格式,如docx这种。

原API提供的都是callback函数,为了使用方便我将其封装成Promise:


/*** get the ooxml of the doc* * * @memberOf WordDocumentService     */getOoxml() {        // Run a batch operation against the Word object model.return Word.run(function (context) {            // Create a proxy object for the document body.var body = context.document.body;            // Queue a commmand to get the HTML contents of the body.var bodyOOXML = body.getOoxml();            // Synchronize the document state by executing the queued commands, // and return a promise to indicate task completion.// return context.sync().then(function () {//     console.log("Body HTML contents: " + bodyHTML.value);//     return bodyHTML.value;// });return context.sync().then(() => { return bodyOOXML.value });}).catch(function (error) {console.log("Error: " + JSON.stringify(error));                if (error instanceof OfficeExtension.Error) {console.log("Debug info: " + JSON.stringify(error.debugInfo));}                return "";});}    /*** set the ooxml of the doc* * @param {string} ooxml * * @memberOf WordDocumentService     */setOoxml(ooxml: string) {        // Run a batch operation against the Word object model.Word.run(function (context) {            // Create a proxy object for the document body.var body = context.document.body;            // Queue a commmand to insert OOXML in to the beginning of the body.            body.insertOoxml(ooxml, Word.InsertLocation.replace);            // Synchronize the document state by executing the queued commands, // and return a promise to indicate task completion.return context.sync().then(function () {console.log('OOXML added to the beginning of the document body.');});}).catch(function (error) {console.log('Error: ' + JSON.stringify(error));                if (error instanceof OfficeExtension.Error) {console.log('Debug info: ' + JSON.stringify(error.debugInfo));}});}

 

当搜索到合适的模板后,可以单击按钮,调用setOoxml()方法,将其插入到当前word文档中:

applyTemplate(template: WordTemplateInfo) {    this.wordDocument.setOoxml(template.TemplateContent);}

 

这样就完成了应用模板的功能。

 

如果要实现将当前文档的内容保存为模板上传到服务器上,就可以调用getOoxml()方法得到当前文档的Ooxml格式文本,上传到服务器保存即可。至于其他的加为收藏、添加为机构模板、设置为个人模板等都是设置模板属性更新了,具体代码不再赘述。

还有一点需要注意的是,开发的时候,这里的服务器地址要写刚才我们开发的ASP.NET Core的地址。

6.使用Fabric UI

对于一个Office Add-in来说,具有简洁美观、与Office统一的UI是必须的。微软推荐使用Fabric UI来实现统一的界面样式,详见:https://dev.office.com/fabric

这里提供了样式、图标、设计规范等很多资源,甚至还提供了React版的组件,如果使用React开发的话直接拿来用就可以了。这个demo是直接引用的style文件,配置在.angular-cli.json文件中:

应用后就变成这样子:

7.打包Add-in

刚才只是在一个新项目里开发了一个静态Web App,还要将其打包,复制到WordTemplateHelperWeb项目中。使用ng build –prod来打包Angular应用。打包后的文件会输出到dist目录下:

注意还有一个需要注意的地方,如果仅这样打包的话,是不支持IE浏览器的,但Office Add-In实际上内置的浏览器就是IE内核,所以我们需要做如下修改,找到src目录中的polyfills.ts文件,将下面部分的注释取消:

还要根据提示,运行npm install命令安装几个必须的依赖。这样才能在IE系列浏览器中正常运行。再次运行ng build –prod进行打包。--prod参数的意义是以生产模式进行build,这样生成的代码体积更小,运行速度更快。

将WordTemplateHelperWeb项目中的原文件除了Web.config外,全部删除。把dist目录中的文件复制过来。

虽然本机开发时可以直接调试运行,但为了模拟真实的使用情况,我们把这个Web App也正式发布一下。如果我们有Azure或其他主机的话就直接部署到服务器上,现在只用本机IIS来承载这个Web App:

这样该Add-In的地址就是:http://localhost/WordTemplateHelperWeb,

下面把api运行起来,进入WordTemplateHelperApi目录,运行dotnet run命令:

这样API项目的地址是:http://localhost:5000/api/

这两个地址不要混淆。刚才在打包WebApp的时候也要注意,在common\app-global.ts文件中的api地址也要改成和实际api地址一样的才可以:

    /*** api url* * @type {string}* @memberOf AppGlobal     */public server: string = "http://localhost:5000/api/WordTemplate";

 

 

现在打开WordTemplateHelperManifest清单文件,修改如下位置:

这里填的是Add-In的地址,一定不要搞错了。

6.运行测试

现在可以重新运行Add-In项目了,将启动项目设置为WordTemplateHelper,运行:

我们可以粘贴一个模板,并上传到服务器上:

点击Upload按钮即可将当前文档作为模板上传到服务器上分享。

搜索到相应的模板后,点击apply按钮即可将模板内容插入到当前文档。

我们可以搜索模板,添加自己的模板,并将模板内容应用到当前文档中。针对组织和个人还可以分别进行管理,我的设想是,这个小插件能够做成一个模板商店之类的平台,用户可以自由的交换彼此的文档模板,并可以收藏、添加到本人组织的模板库中等等。稍加扩展就可以做成一个正式产品了。

7.载入加载动画

在页面加载时可以加一个载入提示,使用户体验更加友好。具体代码可参考index.html中的css样式。

六、小结

这篇文章拖了很久,去年的比赛,今年才把过程整理出来,实在很想对陈老师说一声抱歉^_^。Office Add-In是一个比较新的开发领域,跟以前的开发方式有所不同,但熟悉前端的同学可以迅速进入这个领域,实际上就是写网页。这个实例从后端接口到前台实现,是一个比较完整的项目,希望对Office开发有兴趣的同学下载代码研究一下,开发出更加实用的Add-In。因为这个项目并没有实际部署,所以没有上传到商店中。下载代码的用户请勿用于商业用途。特此说明。 

Github地址:https://github.com/yanxiaodi/WordTemplateHelper

原文地址:http://www.cnblogs.com/yanxiaodi/p/7192280.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/324602.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

秒懂python的深浅copy

dict , list ,set , s {name:alex....} s2 s , 此时s2和s是共享 同一份数据 的&#xff0c;copy一份新数据 , 浅copy, 只copy第一层 s2 s.copy() 深copy s4 copy.deepcopy(s)浅拷贝 深拷贝

SpringBoot整合Shiro实现登录认证和授权CHCache

文章目录一、 springboot实现普通登录1 添加依赖2 编写配置文件3 新建实体类和mapper4 编写业务层代码5 编写控制器6 编写启动类7 编写登录页面和主页面二、 springboot整合shiro实现登录认证和凭证匹配1 添加依赖2 自定义Realm3 编写配置4 userService新增单元方法&#xff1a…

java中如何对对象排序?

大家好&#xff0c;我是雄雄。前言&#xff1a;我们知道&#xff0c;在平时做项目的过程中&#xff0c;我们总会用到各种各样的排序&#xff0c;或是升序&#xff0c;或是降序。在java中&#xff0c;要实现排序有好多中方式&#xff0c;比如我们耳熟能详的冒泡排序、选择排序等…

Java程序员必看的 13 本 Java 书籍

转载自 Java程序员必看的 13 本 Java 书籍 关乎于程序员&#xff0c;除了做项目来提高自身的技术&#xff0c;还有一种提升自己的专业技能就是&#xff1a;多&#xff01;看&#xff01;书&#xff01; 毕竟&#xff0c;书是学习的海洋呢&#xff01;So&#xff0c;Java程序…

祝你生日快乐!

祝生日快乐今天&#xff0c;于我来说是个特别的日子。本来&#xff0c;我是比较低调的&#xff0c;尤其是对于生日来说&#xff0c;可以说是这些年都没有好好的过过&#xff0c;要么忘记了&#xff0c;要么就是在异国他乡的&#xff0c;谁都不识&#xff0c;这一天和往常的一天…

.NET Exceptionless 日志收集框架本地环境搭建

一、简介 Exceptionless 是一个开源的实时的日志收集框架&#xff0c;它可以应用在基于 ASP.NET&#xff0c;ASP.NET Core&#xff0c;Web Api&#xff0c;Web Forms&#xff0c;WPF&#xff0c;Console&#xff0c;MVC 等技术栈的应用程序中&#xff0c;并且提供了Rest接口可以…

java中如何对汉字进行排序?

前言&#xff1a;上一篇文章我们讲解了java中实现Comparator进行排序 和实现Comparable进行排序&#xff0c;我们分别举例根据学号和姓名降序排序&#xff0c;原文在这里&#xff1a;java中如何对对象排序&#xff1f; &#xff0c;不过在文章的最后&#xff0c;我故意留了个问…

Blazor将.NET带回到浏览器

由Steve Sanderson开发的Blazor尝试使用WebAssembly和DotNetAnywhere将.NET带回到浏览器。它不是要成为一个像Flash或Silverlight那样的完整生产框架&#xff0c;而是要探索一下有什么可能性。 WebAssembly将自己描述成一个“内存安全的沙箱执行环境”标准&#xff0c;它“甚至…

过滤器和监听器

文章目录01 过滤器的简介02 使用过滤器进行乱码解决03 使用过滤器进行登陆的控制04 过滤器更多内容05 监听器实现日志记录06 监听器实现在线人数的统计07 其他监听器使用01 过滤器的简介 为什么使用过滤器&#xff1f; 我们目前书写项目中遇到的问题&#xff1f; A、目前使用的…

高启航:秋运会!

本文原创&#xff1a;高启航&#xff08;本文所有收益均归高启航所有&#xff09;在十月份的二十一号&#xff0c;我们济南信息工程学校迎来了一年一度的秋季运动会。这次秋运会&#xff0c;每一个班级所准备的开幕式、训练已久的千人武术大展演、以及在运动会上表现优越的运动…

用python画出吉祥物

大前提 你需要一个"npy" 效果图 源码 # 库的导入 import turtleturtle.title(PythonBingDwenDwen&#xff08;axing&#xff09;) #这里的字我们可以自己进行修改turtle.speed(10) # 速度&#xff0c;如果觉得20快了&#xff0c;你们可以设置10# 左手 turtle.p…

RabbitMQ知多少

1.引言 RabbitMQ——Rabbit Message Queue的简写&#xff0c;但不能仅仅理解其为消息队列&#xff0c;消息代理更合适。RabbitMQ 是一个由 Erlang 语言开发的AMQP&#xff08;高级消息队列协议&#xff09;的开源实现&#xff0c;其内部结构如下&#xff1a; RabbitMQ作为一个…

王宝兴:秋运会

本文原创&#xff1a;王宝兴本文所有收益归王宝兴所有一年一度的运动会如期而至。可谓是“沙场秋点兵”的壮阔&#xff0c;给了我们难以言喻的激情。为了入场式&#xff0c;为了我们每个人&#xff0c;为了我们整个班集体&#xff0c;所有人付出的不止一点&#xff0c;在这短短…

.NET Core快速入门教程 1、开篇:说说.NET Core的那些事儿

一、.NET Core的诞生 聊 .NET Core&#xff0c;就不得不说他的爸爸 .NET。当年Java刚刚兴起&#xff0c;如火如荼&#xff0c;微软也非常推崇Java&#xff0c;当时Windows平台的Java虚拟机就是微软按照JVM标准实现的&#xff0c;据说也是当时性能最好的Java虚拟机。但是微软也…

机器学习和深度学习概念入门

转载自 机器学习和深度学习概念入门 作者&#xff1a;谭东 遵循&#xff1a;BY-SA&#xff08;署名-相同方式共享4.0协议&#xff09; 对于很多初入学习人工智能的学习者来说&#xff0c;对人工智能、机器学习、深度学习的概念和区别还不是很了解&#xff0c;有可能你每天…

lldb 调试 linux下 .net Core 总结及开源扩展 yinuo

相信很多朋友在跟随微软.net core 从windows平台迁移至linux平台的过程中遇到很多别扭的地方&#xff0c;这里我只聊聊 运行时 调试的那些事儿。 首先从工具上来讲Windows上的windbg肯定是运行时的首选调试工具(因为有对应版本的SOS.dll)&#xff0c;在linux平台运行时调试需要…

班级日常分享:一天一瞬间!

好久没有记录班级日常了&#xff0c;自运动会结束之后&#xff0c;事情比较多&#xff0c;也很忙&#xff0c;所以没有及时记录。19级3班最近在上bootstrap&#xff0c;属于前端的范畴&#xff0c;内容较简单&#xff0c;同学们都能跟的上老师的步伐&#xff0c;一行一行大代码…

Java 程序员必须掌握的 8 道数据结构面试题,你会几道

转载自 Java 程序员必须掌握的 8 道数据结构面试题&#xff0c;你会几道 瑞士计算机科学家Niklaus Wirth在1976年写了一本书&#xff0c;名为《算法数据结构编程》。 40多年后&#xff0c;这个等式仍被奉为真理。这就是为什么在面试过程中&#xff0c;需要考察软件工程师对数…

vue.js 01 模板语法

文章目录vue插值vue指令vue插值 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport&…