node.js 组件

在本系列的第一部分中,我们介绍了TransloadIt —一种文件处理服务,专门处理图像,视频和音频。 如果您还没有阅读它,我建议您立即阅读,因为它涵盖了很多背景概念,您需要阅读这些概念才能继续阅读本部分。

但是,有了足够的推理,背景和理论,我们来看一个实际的示例,说明如何使用该服务处理您自己的应用程序中的某些图像。

出于本教程的目的,我们将为任意应用程序实现用户个人资料照片功能。 我们将使用TransloadIt执行以下操作:

  1. 拦截文件上传,而不是将其上传到您的应用程序,而是上传到其服务器。
  2. 执行服务器端文件检查,以确保它满足某些条件,例如它是否确实是映像。
  3. 创建上载图像的许多不同派生形式(例如大小),例如各种尺寸的缩略图以及用户个人资料页面的“中”和“大”版本。
  4. 将所得衍生产品转移到Amazon S3存储桶。
  5. 在我们的应用程序中显示新上传图像的缩略图。
  6. 使用从TransloadIt返回的信息,让我们的应用程序知道在哪里可以找到生成的图像,以便我们可以在用户记录中存储对它们的引用。

第一步是构建一些包含汇编指令的模板。

模板入门

模板包含JSON格式的汇编指令。 启动您喜欢的文本编辑器,开始一些JSON:

{
}

……让我们深入。

筛选档案

首先,我们将添加一个步骤,该步骤利用/ file / filter机器人检查上传的文件的MIME类型,以确保它是图像。 将以下内容添加到您的空JSON文档中:

"steps":
"files": {
"robot": "/file/filter",
"accepts": [
[
"${file.mime}",
"regex",
"image"
]
],
"error_on_decline": true
},

让我们分解一下。

我们从密钥files确定的步骤开始。 您可以随心所欲地对其进行命名,但是在大多数情况下, files才有意义。

接下来,我们告诉TransloadIt使用/file/filter/机械手,该机械手用于对传入文件进行一些检查。 在这种情况下,我们告诉它要接受什么。 我们要求它提取文件的MIME类型并在其上运行一个正则表达式( image )。

在TransloadIt指令中,变量使用美元符号和大括号${} 。 指定正则表达式时,只需要指定它的主体即可。

如果我们设置的测试失败,则error_on_decline参数可确保引发错误,而不是继续进行下一步。

换句话说,我们告诉TransloadIt拒绝所有不是图像的文件。

添加调整大小步骤

现在,让我们再创建三个步骤,每个步骤都做同样的事情-创建传入图像的派生(即特定大小)。

我们可以随意调用这些步骤,因此我们将使用为派生类提供一些上下文的名称mediumlargethumbnail

让我们定义以下步骤的第一步:

"medium": {
"use": ":original",
"robot": "/image/resize",
"width": 300,
"height": 200,
"resize_strategy": "fit"
},

在这里,我们定义了一个名为medium的步骤,该步骤利用了/image/resize机械手。 这需要许多参数,其中许多是可选的,这些参数在此处记录 。

use参数告诉它调整原始文件的大小。

在这种情况下,我们将提供所需的尺寸-300 x 200像素-并指定调整大小策略。 可用的调整大小策略记录在这里 ,但本质上fit确保了图像被调整大小以适应指定的尺寸,同时保持高宽比。

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

免费获得这本书

large步骤实际上是相同的:

"large": {
"use": ":original",
"robot": "/image/resize",
"width": 480,
"height": 320,
"resize_strategy": "fit"
},

然后thumbnail步骤:

"thumbnail": {
"use": ":original",
"robot": "/image/resize",
"width": 80,
"height": 80,
"resize_strategy": "crop"
},

这次我们使用crop策略来确保最终得到的图像是完美的正方形。

为了使过程更高效,没有理由不设置use参数来告诉TransloadIt将缩略图基于已处理的大中型版本。

在此阶段,我们确保正在处理图像,并且已将其大小调整了三倍,以创建三个单独的图像导数。 接下来,我们将告诉TransloadIt如何处理新创建的派生类。

出口

如前所述,Transloadit不会长时间存储我们的文件-托管不是服务的全部内容-因此我们需要将文件移到更永久的位置。

我们将使用/s3/store文件导出机器人将文件上传到Amazon S3存储桶。

这是配置该步骤的方法:

"export": {
"use": [
"medium",
"large",
"thumbnail"
],
"robot": "/s3/store",
"path": "users/profiles/${fields.username}_${previous_step.name}.${file.ext}",
"key": "YOUR-S3-AUTH-KEY",
"secret": "YOUR-S3-AUTH-SECRET",
"bucket": "YOUR-BUCKET-NAME"
}

您需要用自己的S3凭据和存储桶名称替换。

这比我们之前的步骤稍微复杂一些,所以让我们分解一下。

use参数告诉机器人对每个调整大小后的图像执行此步骤,从而在S3上为每次上传生成三个文件。 如您所见, mediumlargethumbnail匹配我们前面三个步骤的标识符。

然后,我们指定密钥(S3的路径术语),用于使用path配置值存储生成的文件。 此名称与存储桶的完全限定域名结合在一起,随后成为生成的派生图像的URI。

在上面的示例中,我们使用以下模式:

users/profiles/${fields.username}_${previous_step.name}.${file.ext}

这种模式首先在路径前面加上users/profiles/ ,然后使用一个名为username的隐藏表单字段的值,我们将在稍后定义它。 然后将其与定义上一步的键(即我们的派生名称)连接起来。 最后,它添加了原始文件的扩展名,可以通过${file.ext}变量访问该扩展名。

那是一个很大的嘴,所以也许最好用一个例子来说明。 给定用户名bob ,此模式将产生以下三个路径:

users/profiles/bob_medium.jpg
users/profiles/bob_large.jpg
users/profiles/bob_thumbnail.jpg

通过砍切和更改可用的变量,您可以采用各种命名策略。 敌人的例子,请考虑以下模式:

users/profiles/${fields.username}${file.meta.width}x${file.meta.width}.${file.ext}

通过串联用户名,结果文件的宽度和高度以及最后文件的扩展名,可以动态构造文件名。 这将导致如下所示:

users/profiles/bob480x320.jpg

请注意,如果图像小于导数的目标尺寸,则这些值将反映最终图像,而不是配置的尺寸。

要简单地使用原始文件名:

${file.name}

为了确保唯一性,以下变量提供了唯一的32个字符的前缀:

${unique_prefix}

有关可用变量的完整列表,请参阅文档中有关汇编程序变量的部分 。

上载范本

将所有这些步骤放在一起,组成模板的组装说明如下所示:

{
"steps": {
"files": {
"robot": "/file/filter",
"accepts": [
[
"${file.mime}",
"regex",
"image"
]
],
"error_on_decline": true
},
"medium": {
"use": ":original",
"robot": "/image/resize",
"width": 300,
"height": 200,
"resize_strategy": "fit"
},
"large": {
"use": ":original",
"robot": "/image/resize",
"width": 480,
"height": 320,
"resize_strategy": "fit"
},
"thumbnail": {
"use": ":original",
"robot": "/image/resize",
"width": 80,
"height": 80,
"resize_strategy": "crop"
},
"export": {
"use": [
"medium",
"large",
"thumbnail"
],
"robot": "/s3/store",
"path": "users/profiles/${fields.username}_${previous_step.name}.${file.ext}",
"key": "YOUR-S3-AUTH-KEY",
"secret": "YOUR-S3-AUTH-SECRET",
"bucket": "YOUR-BUCKET-NAME"
}
}
}

在适当的位置输入您自己的S3凭据,然后我们准备将模板上传到TransloadIt。

在本教程随附的示例代码存储库中,您可以在名为template.json的文件中找到上述JSON。

如果您尚未使用TransloadIt创建帐户, 则现在需要这样做 。

您需要登录; 然后转到您的信息中心(我的帐户)。 在左侧边栏中的集成下,选择模板 。 然后,单击右上角的“ 新建”按钮。

系统会要求您提供一个名称来标识您的模板-像user_avatars这样的东西就可以了。 然后,粘贴到上面的JSON(您还将在本文随附的存储库的根目录中找到该JSON)中-确保已用您自己的JSON替换了虚拟S3值,然后点击Save

如果您想使用其他存储机制,例如(S)FTP或Rackspace Cloud Files,则可以在此处找到相关文档-只需相应地修改最后一步即可。

您将被带回到主模板文件,并且您会注意到为新创建的模板分配了一个哈希作为唯一ID。 记下这一点,因为稍后将需要它。

完成之后,让我们看一下如何从应用程序中使用TransloadIt。

示例应用

您可以在Github上找到一个示例应用程序来伴随本教程。

为了运行它,您需要确保已安装以下先决条件:

  • Node.js
  • npm
  • MongoDB
  • 凉亭

Vagrant用户将在存储库中找到一个Vagrant Vagrantfile ,以创建包含所有列出的依赖项的VM。

本质上,它是一个简单的Express应用程序。 为简洁起见,我们在这里不涉及很多内容:

  • 它使用config模块将应用程序的配置保存在.yaml文件中。
  • 它使用Mongoose和MongoDB来定义用户模型。
  • 它使用Passport和Local策略来提供简单的身份验证机制。
  • 它提供了中间件来安全地对密码进行哈希处理。
  • 它包含一些简单的中间件,以将某些路由限制为仅通过身份验证的用户。
  • 它使用Handlebars,以及handlebars-layouts包来处理模板。

首先,克隆应用程序,然后安装依赖项:

npm install
bower install

该应用程序中有几个元素值得简要介绍。

这是User模型的架构定义:

var userSchema = mongoose.Schema({
username : { type: String, required: true, unique: true },
email    : { type: String, required: true, unique: true },
password : { type: String, required: true },
avatar   : { type: mongoose.Schema.Types.Mixed, required: false }
});

注意我们如何包含类型为Mixedavatar字段。 这将使我们能够将化身指定为哈希,例如:

user.avatar = {
thumbnail : 'http://your.bucket.name.aws.amazon.com/user/profile/bob_thumbnail.jpg',
medium    : 'http://your.bucket.name.aws.amazon.com/user/profile/bob_medium.jpg',
large     : 'http://your.bucket.name.aws.amazon.com/user/profile/bob_large.jpg'
};

现在基本结构已经就绪,让我们看一下TransloadIt的jQuery插件。

jQuery插件

在客户端与TransloadIt集成的最简单方法是使用官方的jQuery插件 ,尽管还有其他替代方法,我们将在本文后面看到。

可通过以下URL获得该插件的最新版本:

https://assets.transloadit.com/js/jquery.transloadit2-latest.js

最小集成涉及以下步骤:

  • 您将插件绑定到表单
  • 插件“劫持”表单提交,直接将文件发送到Transloadit
  • 插件等待,直到文件已上传并处理
  • Transloadit返回带有结果的JSON对象,该对象还将包含新生成文件的URL
  • 它会创建一个隐藏的textarea元素,其中包含来自Transloadit的JSON
  • 表格已提交给您的申请

这是初始化插件,告诉它使用模板的非常简单的示例:

$(function() {
$('#upload-form').transloadit({
wait: true,
params: {
auth: {
key: 'YOUR-AUTH-KEY'
},
template_id: 'YOUR-TEMPLATE-ID'
}
});
});

但是,正如我们在上一部分中指出的那样,在客户端代码中公开身份验证凭据不是一个好主意。 相反,我们将使用签名。

签名

签名是使用身份验证令牌的一种更安全的选择,尽管它们需要一些服务器端的工作。

本质上,使用签名要求您不要对客户端应用程序发送一堆指令到TransloadIt,而是对指令进行编码,并使用HMAC算法和专用认证密钥对它们进行加密。 结果生成了一个临时令牌(即签名),该令牌仅限于特定的指令组合。 因为它是临时的,所以如果该令牌被破坏,那么它将很快变得无用。

您不必担心自己生成签名的来龙去脉,因为我们可以使用第三方库来处理该过程。 如果您使用的是Node.js,则官方SDK将为您处理。

要安装库:

npm install transloadit --save

您需要身份验证密钥和身份验证密钥,可以从TransloadIt网站上的“ API凭据”部分获得。 将它们放在config\default.yaml的相关部分。

您需要通过将RENAME_THIS_TO_default.yaml重命名或复制到default.yaml来创建默认配置文件。

现在,创建TransloaditClient类的实例,并为其提供身份验证详细信息:

var TransloaditClient =   require('transloadit');
var transloadit       =   new TransloaditClient({
authKey     : config.transloadit.auth_key,
authSecret  : config.transloadit.auth_secret
});

接下来,定义要执行的操作的参数。 可以采用一组汇编指令的形式:

var params = {
steps: {
// ...
}
};

或者,就我们而言,我们只需提供模板的ID:

var params = {
template_id: 'YOUR-TEMPLATE-ID'
};

要创建签名:

var sig = transloadit.calcSignature(params);

这将导致散列包含签名(各种访问令牌)以及调用服务所需的参数。 因此,我们的sig对象将如下所示:

{
signature: "fec703ccbe36b942c90d17f64b71268ed4f5f512",
params: {
template_id: 'YOUR-TEMPLATE-ID',
auth: {
key: 'idfj0gfd9igj9dfjgifd8gfdj9gfdgf',
expires: '2015-06-25T10:05:35.502Z'
}
}
}

为了将其传递给我们的Handlebars模板,以便我们JavaScript可以利用它,我们需要创建一个非常简单的助手:

app.engine('.hbs', exphbs(
{
extname: '.hbs',
defaultLayout: 'default',
helpers : {
json : function(context) {
return JSON.stringify(context);
}
}
}
));

现在让我们一起来定义account路由,其中​​将包括我们的头像上传表格:

// The account page
app.get('/account', ensureAuthenticated, function(req, res){
// Require the TransloadIt client
var TransloaditClient = require('transloadit');
// Create an instance of the client
var transloadit       =   new TransloaditClient({
authKey     : config.transloadit.auth_key,
authSecret  : config.transloadit.auth_secret
});
// Build the Transloadit parameters...
var params = {
template_id     :   config.transloadit.template_id
};
// ...and generate the signature
var sig = transloadit.calcSignature(params);
return res.render('account', {
user: req.user,
sig : sig
});
});

然后,在相应的模板( views/account.hbs )中,让我们从一些非常简单HTML开始:

<h2>Hello, {{ user.username }}</h2>
{{# if user.avatar }}
<img src="{{ user.avatar.thumbnail }}" id="avatar">
{{else}}
<img src="/avatar.png" id="avatar">
{{/if}}
<form method="POST" action="/avatar" id="avatar-form">
<input type="file" name="image" id="avatar-upload">
<input type="hidden" name="username" value="{{user.username}}">
</form>

请注意,我们包括一个包含用户名的隐藏字段。 我们将根据请求将其发送到TransloadIt,以便可以在我们的模板中使用它。

现在添加JavaScript,首先使用我们的json Handlebars帮助器进行一些变量初始化:

var sig = {{{ json sig }}};

现在,我们将TransloadIt插件绑定到上传表单:

$(function() {
$('#avatar-form').transloadit({
wait: true,
params: JSON.parse(sig.params),
signature: sig.signature,
fields: true,
triggerUploadOnFileSelection: true,
autoSubmit: false,
onSuccess: function(assembly) {
$('img#avatar').attr('src', assembly.results.thumbnail[0].url + '?' + (new Date()).getTime() );
var derivatives = {
thumbnail : assembly.results.thumbnail[0].url,
medium : assembly.results.medium[0].url,
large : assembly.results.large[0].url
};
$.ajax({
type: 'post',
url: '/avatar',
data: derivatives,
success: function(resp){
console.log(resp);
}
})
}
});
});

这比我们之前介绍的最小集成初始化要复杂得多,因此让我们一次来进行一下了解。

我们从sig变量中获取参数和签名,该变量是在服务器上生成的,然后编码为JSON。 因为params部分是嵌套的,所以我们使用JSON.parse()将其转换回一个对象,然后TransloadIt将从中提取相关参数。

在插件初始化中, wait设置为true ,这意味着我们要等到两个文件都已上传并且已对其进行处理。

使用程序集通知 (您可以在稍后的“高级用法”部分中进行了解)意味着您不必等待文件被处理,在这种情况下,您可以将wait设置为false

fields设置为true可以告诉插件我们在发送文件进行处理时希望包括其他信息; 在我们的例子中,这是一个名为username的隐藏表单字段,我们使用经过身份验证的用户的username填充该字段。

一旦用户选择了文件,而不是提交表单时,使用triggerUploadOnFileSelection将文件发送到Transloadit。 一旦结果从Transloadit返回时, autoSubmit阻止它提交表单,因为我们将自己手动进行操作。

当数据从Transloadit返回时,将触发onSuccess回调,这为我们提供了assembly中数据的哈希值。

assembly对象包含一个results属性,该属性又包含我们每个“步骤”的属性。 这些包含文件对象数组。 由于我们仅上传一个文件,因此它们将是包含单个项目的数组。 每个文件对象都包含许多属性,包括原始文件名,元信息,Transloadit的唯一ID以及其他细节。 要查看全部信息,您可能希望将其注销到控制台中并进行查看。 但是,我们真正感兴趣的只是url属性,其中包含S3上生成的图像的URL。

或者,您可能希望使用ssl_url属性,该属性与url相同,但通过HTTPS。

我们只是通过对应的派生名称提取三个URL,然后创建这三个派生及其对应URL的哈希。

为了向用户提供视觉反馈,我们还获取了缩略图的URL并修改页面上的头像以显示新上传的图像。

最后,我们使用Ajax将数据以静默方式发布回我们的应用程序。

这是捕获该数据的avatar路线:

// Ajax callback for setting the avatar
app.post('/avatar', ensureAuthenticated, function(req, res){
req.user.avatar = req.body
req.user.save(function(err) {
if(err) {
return res.send('error');
}
return res.send('ok');
});
});

在生产中,您可能需要对此进行清理和验证。

如您所见,我们获取派生图像及其URL的哈希,从req.user获取当前经过身份验证的用户,将avatar属性设置为提供的哈希,然后更新用户模型。

这只是一种可能的方法。 为了获得更快的反馈,您可能希望使用插件的onResult回调在缩略图生成后立即获取它,而不是等待所有三个派生类。 与其使用客户端代码中的Ajax调用来通知服务器,不如选择使用Assembly通知功能,该功能提供了在后台运行程序集的其他好处,而不是在客户端上阻止执行。 请查阅插件文档以获取所有选项。

到此结束我们的基本应用。 别忘了,所有源(包括身份验证机制)都在Github上结束了 。

高级用法

在总结之前,让我们简单地看一下TransloadIt的两个更高级的方面。

其他客户端选项

您不必使用提供的jQuery插件。 在文档的“ 社区项目”部分中,您可以找到许多替代方法,包括Bootstrap插件, drag n'drop插件, Angular插件或对简单的旧XHR的支持。

您可能需要更详细地了解XHR。 这是一个简单的解决方案,提供了很大的灵活性,同时要求您提供自己的反馈(例如某种上传指示器)。 还值得注意的是,一旦上传了文件,它将尝试通过以1000ms的间隔轮询服务器来确定何时完成了程序集。

通知事项

当文件准备好时,您可以使用通知来ping您的应用程序,而不必让用户等待处理他们的上载。 使用这种方法,用户只需等到上传完成即可。

从消费者的角度来看,通知很容易实现; 只需在您的汇编指令中包含notify_url即可,例如:

{
auth       : { ... },
steps      : { ... },
notify_url : "http://example.com/webhooks/incoming/transloadit"
}

当您的URL被Transloadit ping通时,提供的JSON将包含一个signature字段,您可以使用该字段来验证通知确实来自他们。 只需使用您的身份验证密钥对签名进行解码即可。

在开发过程中,您可能希望利用此代理软件包来测试程序集通知,或使用隧道服务(例如ngrok) 。

摘要

在这个由两部分组成的系列文章中,我们对文件处理服务TransloadIt进行了全面介绍。

在第一部分中,我们研究了一些优点和缺点,然后研究了关键概念。

这一部分,我们动手了,并使用jQuery,Node.js和Express构建了一个简单的用户头像组件。

您不仅限于jQuery,而且确实可以自由使用原始JavaScript解决方案或您喜欢的框架。 您甚至不需要在客户端应用程序中使用它,而在服务器端技术方面,您可以选择很多。 不过,希望您现在对如何将其用于图像处理有所了解。

您在项目中使用TransloadIt吗? 您知道更好的服务吗? 在评论中让我知道。

翻译自: https://www.sitepoint.com/user-avatar-component-node-js-transloadit/

node.js 组件

node.js 组件_使用Node.js和TransloadIt构建用户头像组件相关推荐

  1. 使用Node.js和TransloadIt构建用户头像组件

    在本系列的第一部分中,我们介绍了TransloadIt -一种文件处理服务,专门处理图像,视频和音频. 如果您还没有阅读它,我建议您立即阅读,因为它涵盖了很多背景概念,您需要阅读这些概念才能继续阅读本 ...

  2. 【node.js后台api项目】(七)更新用户头像接口

    [node.js后台api项目](七)更新用户头像接口 1.接口相关信息 2. 定义路由和处理函数 3.验证提交的数据 4.实现更新用户基本信息功能 1.接口相关信息 路由: /my/update/a ...

  3. node.js事件驱动_了解Node.js事件驱动架构

    node.js事件驱动 by Samer Buna 通过Samer Buna 了解Node.js事件驱动架构 (Understanding Node.js Event-Driven Architect ...

  4. react 验证码组件_使用React.JS和Twilio服务创建电话号码验证组件。

    react 验证码组件 介绍 (Introduction) Phone number verification is required for phone sign-in or Two Factor ...

  5. 动态添加组件_使用vue.js的动态组件模板

    最近刚做完建站工具,准备总结里面使用到的一些技巧,同时会做一版简化的放在 github上. 先来一篇翻译的文章,和我在项目中使用的动态组件思路一样,不过缺少了演化的过程,直接给出了最终的解决方案.这篇 ...

  6. html调用js函数_使用Require.js实现模块化开发

    在javascript中,我们把比较复杂的都是用模块化,今天我们就来了解一下require.js来实现模块化开发 require.js是什么? require.js是在AMD规范上实现的一个 Java ...

  7. java创建node类型数据类型_[Java教程]js DOM Node类型

    [Java教程]js DOM Node类型 0 2015-12-18 16:00:08 DOM(文档对象模型)是针对HTML和 DOM可以将任何HTML或 以下面为例: My article Hell ...

  8. uibot在子程序执行js失败_使用 Node.js 将珍藏的 bash 脚本封装成命令行工具

    阐述如何将一个常用的 bash 脚本融入 npm 生态之中,此处以最近遇到的一个 CR 提交脚本为例. 背景 作为程序猿,大家或多或少地都用过 GitHub 上的 merge request 功能.当 ...

  9. 查看node的位置_升级Node版本RN项目运行报错cb.apply is not a function

    今日打算安装一下ReactNative官方推荐的脚手架工具Ignite. infinitered/ignite​github.com Ignite是一套整合了 Redux 以及一些常见 UI 组件的脚 ...

最新文章

  1. 181920.net用 文字存档
  2. nohup 命令 用途:不挂断地运行命令
  3. 三种强大的物体识别算法——SIFT/SURF、haar特征、广义hough变换的特性对比分析
  4. matplotlib——在 Jupyter Notebook中绘制图像时只显示变量信息不显示图片
  5. NLP—3.文本特征工程及工具使用举例
  6. 基于Struts实现用户登录和注册模块
  7. 执行查看linux端口命令 9083 端口发现被占用 Hive安装过程遇到的问题
  8. python飞机大战项目概述需求_飞机大战需求分析报告.doc
  9. ie浏览器css怎么调,IE浏览器下的CSS问题小结
  10. Centos8创建pem文件进行远程连接
  11. 计算机快速启动BIOS,bios设置快速启动
  12. Ubuntu安装Linux网页版微信
  13. 将RT-Thread Nano移植到STM32F401CCU6
  14. 第一步:文献阅读与翻译
  15. 使用Photoshop制作相框
  16. windows截图指令命令/cmd中截图指令
  17. 浮点数修约的法则c语言,IEEE754浮点表示法详解
  18. 《Python股票量化交易从入门到实践》随书赠送“回测框架”的使用帮助
  19. 支付宝扫码支付示例源码
  20. 计算机基础 CMOS

热门文章

  1. 微积分与概率论的基础知识
  2. tomcat定时自动重启设置方法
  3. 5G风口短信“变脸”求生,三大运营商要联手战微信?
  4. 小编推荐这款,超实用的SpringBoot 开源商城系统,挣钱不是太轻松!
  5. MIT的《深度学习》精读(17)
  6. 论文阅读笔记:SCAN: Learning to Classify Images without Labels
  7. android 红包功能,Android 微信抢红包实现
  8. 最大公共字符串,最大公共子序列,编辑距离,myers等算法
  9. 如何自己写一门简单的编程语言(解释型语言)
  10. 史上最简单:SpringCloud 集成 mybatis-plus(以若依微服务版本为例)