当前位置: 首页 > news >正文

【分布式云储存】Springboot微服务接入MinIO实现文件服务

文章目录

    • 前言
    • 技术回顾
    • 准备工作
      • 申请accessKey\secretKey
      • 创建数据存储桶
      • 公共资源直接访问测试
    • 接入springboot实现文件服务
      • 依赖引入
      • 配置文件
      • MinIO配置
      • MinIO工具类
    • OkHttpSSLSocketClient兼容ssl
    • 静态资源预览解决方案
    • 资源上传预览测试
    • 测试结果

前言

上篇博客我们介绍了分布式云存储MinIO作业环境的搭建,以及分布式云储存MinIO在实际的文件服务中的优势。那么,今天我们就小试牛刀来将MinIO接入我们的微服务项目,实现一个分布式的文件服务器。

技术回顾

MinIO 提供高性能、与S3 兼容的对象存储系统,让你自己能够构建自己的私有云储存服务。
MinIO原生支持 Kubernetes,它可用于每个独立的公共云、每个 Kubernetes 发行版、私有云和边缘的对象存储套件。
MinIO是软件定义的,不需要购买其他任何硬件,在 GNU AGPL v3 下是 100% 开源的。

准备工作

申请accessKey\secretKey

MinIO控制台申请accessKey\secretKey
http://your_hostname:18001~18004/login minio/minio123
在这里插入图片描述

创建数据存储桶

创建数据存储桶
在这里插入图片描述在这里插入图片描述

点击创建的桶我们进行访问策略配置
在这里插入图片描述
在这里插入图片描述

访问策略有private\custom\public
public 公共的桶,任何人都可以访问资源,直接映射为静态资源,可直接提供预览和下载
custom 自定义桶,用户根据自身需求定义访问规则
private 私有的桶,需要授权才能访问

根据一般的生产需求,我们定义一个private,一个custom桶。private保存私有资源,custom保存公共资源并禁止目录访问。
在这里插入图片描述

private 规则:
在这里插入图片描述

custom 规则:
在这里插入图片描述

{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Principal": {"AWS": ["*"]},"Action": ["s3:GetBucketLocation","s3:ListBucketMultipartUploads"],"Resource": ["arn:aws:s3:::sacpublic"]},{"Effect": "Allow","Principal": {"AWS": ["*"]},"Action": ["s3:AbortMultipartUpload","s3:DeleteObject","s3:GetObject","s3:ListMultipartUploadParts","s3:PutObject"],"Resource": ["arn:aws:s3:::sacpublic/*"]}]
}

公共资源直接访问测试

私有资源代码博文最后测试
公共资源直接访问测试:
在这里插入图片描述
在这里插入图片描述

接入springboot实现文件服务

依赖引入

maven引入依赖

<!--minio-->
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.0</version>
</dependency>
<!--minio-->

配置文件

配置文件

minio.url = https://10.10.22.91:9100
minio.accessKey = fUIXbkBZ9UQTHOOZXNGW
minio.secretKey = sy1RAgItAOk9pk1gE7FbrPYzsZI87CfpGkuoY0KW
minio.buckets.public = sacpublic
minio.buckets.private = sacprivate

注意:接口调用minio url 为nginx映射出来的9000端口
http://your_hostname:9000

MinIO配置

MinIO配置

/*** MinioConfig* @author senfel* @version 1.0* @date 2023/9/14 11:37*/
@Configuration
@ConditionalOnProperty(name = "oss.file.service", havingValue = "minio", matchIfMissing = true)
public class MinioConfig {@Value("${minio.url}")private String minioUrl;@Value("${minio.accessKey}")private String accessKey;@Value("${minio.secretKey}")private String secretKey;@Beanpublic MinioClient getMinioClient() {return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build();}}

MinIO工具类

MinIO工具类

/*** MinioUtil* @author senfel* @version 1.0* @date 2023/9/14 10:26*/
@Component
@ConditionalOnProperty(name = "oss.file.service", havingValue = "minio", matchIfMissing = true)
public class MinioUtil {@Resourceprivate MinioClient minioClient;/*** 创建一个桶*/public void createBucket(String bucket) throws Exception {boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());if (!found) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());}}/*** 上传一个文件*/public ObjectWriteResponse uploadFile(InputStream stream, String bucket, String objectName) throws Exception {String prefix = objectName.substring(objectName.lastIndexOf(".") + 1);//静态资源预览解决方案//String contentType = ViewContentType.getContentType(prefix);ObjectWriteResponse objectWriteResponse = minioClient.putObject(PutObjectArgs.builder().bucket(bucket).object(objectName).contentType(contentType).stream(stream, -1, 10485760).build());return objectWriteResponse;}/*** 列出所有的桶*/public List<String> listBuckets() throws Exception {List<Bucket> list = minioClient.listBuckets();List<String> names = new ArrayList<>();list.forEach(b -> {names.add(b.name());});return names;}/*** 下载一个文件*/public InputStream download(String bucket, String objectName) throws Exception {InputStream stream = minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(objectName).build());return stream;}/*** 删除一个桶*/public void deleteBucket(String bucket) throws Exception {minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucket).build());}/*** 删除一个对象*/public void deleteObject(String bucket, String objectName) throws Exception {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objectName).build());}/*** 复制文件** @Param: [sourceBucket, sourceObject, targetBucket, targetObject]* @return: void* @Date: 2021/11/15*/public void copyObject(String sourceBucket, String sourceObject, String targetBucket, String targetObject) throws Exception {this.createBucket(targetBucket);minioClient.copyObject(CopyObjectArgs.builder().bucket(targetBucket).object(targetObject).source(CopySource.builder().bucket(sourceBucket).object(sourceObject).build()).build());}/*** 获取文件信息** @Param: [bucket, objectName]* @return: java.lang.String*/public String getObjectInfo(String bucket, String objectName) throws Exception {return minioClient.statObject(StatObjectArgs.builder().bucket(bucket).object(objectName).build()).toString();}/*** 生成一个给HTTP GET请求用的presigned URL。浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。* @Param: [bucketName, objectName, expires]* @return: java.lang.String*/public String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) throws Exception {GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).expiry(expires).method(Method.GET).build();return minioClient.getPresignedObjectUrl(build);}}

OkHttpSSLSocketClient兼容ssl

对于改解决方案一般生产环境都有固定的域名和匹配的ssl证书,如果也不用https我们代码则不用兼容ssl。但是如果我们配置了不可信任的ssl,这里我们则需要进行ssl兼容方案。

/*** MinioConfig* @author senfel* @version 1.0* @date 2023/9/14 11:37*/
@Configuration
@ConditionalOnProperty(name = "oss.file.service", havingValue = "minio", matchIfMissing = true)
public class MinioConfig {@Value("${minio.url}")private String minioUrl;@Value("${minio.accessKey}")private String accessKey;@Value("${minio.secretKey}")private String secretKey;@Beanpublic MinioClient getMinioClient() {OkHttpClient okHttpClient = new OkHttpClient.Builder().sslSocketFactory(OkHttpSSLSocketClient.getSSLSocketFactory(),OkHttpSSLSocketClient.getX509TrustManager()) // //通过sslSocketFactory方法设置https证书.hostnameVerifier(OkHttpSSLSocketClient.getHostnameVerifier()).build();return MinioClient.builder().endpoint(minioUrl).httpClient(okHttpClient).credentials(accessKey, secretKey).build();}}
/*** OkHttpSSLSocketClient* @author senfel* @version 1.0* @date 2023/9/15 10:07*/
public class OkHttpSSLSocketClient{//获取SSLSocketFactorypublic static SSLSocketFactory getSSLSocketFactory() {try {SSLContext sslContext = SSLContext.getInstance("SSL");sslContext.init(null, getTrustManager(), new SecureRandom());return sslContext.getSocketFactory();} catch (Exception e) {throw new RuntimeException(e);}}//获取TrustManagerprivate static TrustManager[] getTrustManager() {TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) {}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) {}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[]{};}}};return trustAllCerts;}//获取HostnameVerifier,验证主机名public static HostnameVerifier getHostnameVerifier() {HostnameVerifier hostnameVerifier = (s, sslSession) -> true;return hostnameVerifier;}//X509TrustManager:证书信任器管理类public static X509TrustManager getX509TrustManager() {X509TrustManager x509TrustManager = new X509TrustManager() {//检查客户端的证书是否可信@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) {}//检查服务器端的证书是否可信@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) {}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}};return x509TrustManager;}
}

静态资源预览解决方案

由于我们使用API上传,minio不能区分我们资源的类型,如果资源类型不对则不能正确提供预览功能,都直接变成为访问资源链接就下载了。

所以,在实际的项目集成中我们在上传资源前需要将资源类型写入请求中,方便minio解析并提供预览和下载功能。

/*** 上传一个文件*/
public ObjectWriteResponse uploadFile(InputStream stream, String bucket, String objectName) throws Exception {String prefix = objectName.substring(objectName.lastIndexOf(".") + 1);//静态资源预览解决方案String contentType = ViewContentType.getContentType(prefix);ObjectWriteResponse objectWriteResponse = minioClient.putObject(PutObjectArgs.builder().bucket(bucket).object(objectName).contentType(contentType).stream(stream, -1, 10485760).build());return objectWriteResponse;
}
/*** ViewContentType* @author senfel* @version 1.0* @date 2023/9/14 17:27*/
public enum ViewContentType {DEFAULT("default","application/octet-stream"),JPG("jpg", "image/jpeg"),TIFF("tiff", "image/tiff"),GIF("gif", "image/gif"),JFIF("jfif", "image/jpeg"),PNG("png", "image/png"),TIF("tif", "image/tiff"),ICO("ico", "image/x-icon"),JPEG("jpeg", "image/jpeg"),WBMP("wbmp", "image/vnd.wap.wbmp"),FAX("fax", "image/fax"),NET("net", "image/pnetvue"),JPE("jpe", "image/jpeg"),RP("rp", "image/vnd.rn-realpix");private String prefix;private String type;public static String getContentType(String prefix){if(StringUtils.isEmpty(prefix)){return DEFAULT.getType();}prefix = prefix.substring(prefix.lastIndexOf(".") + 1);for (ViewContentType value : ViewContentType.values()) {if(prefix.equalsIgnoreCase(value.getPrefix())){return value.getType();}}return DEFAULT.getType();}ViewContentType(String prefix, String type) {this.prefix = prefix;this.type = type;}public String getPrefix() {return prefix;}public String getType() {return type;}
}

资源上传预览测试

MinIO对于图片等资源可以直接预览,对于excel文档等直接提供下载功能

@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class CodeDbInfoServiceTests  {@Resource@Lazyprivate MinioUtil minioUtil;/*** upload* @author senfel* @date 2023/9/27 10:00* @return void*/@Testpublic void upload() throws Exception {FileInputStream fileInputStream = new FileInputStream("C:/Users/dev/Desktop/minio.png");ObjectWriteResponse sacprivate = minioUtil.uploadFile(fileInputStream, "sacprivate", "test/minio.png");System.err.println(sacprivate.bucket());}/*** 获取私库连接* @author senfel* @date 2023/9/27 10:01* @return void*/@Testpublic void getPrivateUrl() throws Exception{String url = minioUtil.getPresignedObjectUrl("sacprivate", "test/minio.png", 60);System.err.println(url);}
}

测试结果

图片成功上传
在这里插入图片描述

私库直接预览访问失败
在这里插入图片描述

获取私库链接过期时间为60s
在这里插入图片描述

访问成功
在这里插入图片描述

60s后再次访问该链接,提示链接已经过期
在这里插入图片描述

至此minio接入并测试完成,对于公开桶的资源直接可任意访问不用获取有效期链接。

写在最后
Springboot微服务接入MinIO实现文件服务较为简单,我们只要按照官方文档引入依赖调用即可。值得注意的是我们根据需求选择接入ssl兼容方案和静态资源预览功能。当然,minio的private\custom\public可以完全实现我们各种需求,可以完全替代市场上付费和笨重的分布式服务。

相关文章:

【分布式云储存】Springboot微服务接入MinIO实现文件服务

文章目录 前言技术回顾准备工作申请accessKey\secretKey创建数据存储桶公共资源直接访问测试 接入springboot实现文件服务依赖引入配置文件MinIO配置MinIO工具类 OkHttpSSLSocketClient兼容ssl静态资源预览解决方案资源上传预览测试测试结果 前言 上篇博客我们介绍了分布式云存…...

机器人中的数值优化|【四】L-BFGS理论推导与延伸

机器人中的数值优化|【四】L-BFGS理论推导与延伸 往期内容回顾 机器人中的数值优化|【一】数值优化基础 机器人中的数值优化|【二】最速下降法&#xff0c;可行牛顿法的python实现&#xff0c;以Rosenbrock function为例 机器人中的数值优化|【三】无约束优化&#xff0c;拟牛…...

ThemeForest – Canvas 7.2.0 – 多用途 HTML5 模板

ThemeForest 上的 HTML 网站模板受到全球数百万客户的喜爱。与包含网站所有页面并允许您在 WP 仪表板中自定义字体和样式的 WordPress 主题不同&#xff0c;这些设计模板是用 HTML 构建的。您可以在 HTML 编辑器中编辑模板&#xff0c;但不能在 WordPress 上编辑模板&#xff0…...

本地部署 川虎 Chat

本地部署 川虎 Chat 1. 川虎 Chat 项目概述2. Github 地址3. 部署 川虎 Chat4. 配置 config.json5. 启动 川虎 Chat 1. 川虎 Chat 项目概述 为ChatGPT等多种LLM提供了一个轻快好用的Web图形界面和众多附加功能。 支持 GPT-4 基于文件问答 LLM本地部署 联网搜索 Agent 助理…...

IntelliJ IDEA 控制台中文乱码的四种解决方法

前言 IntelliJ IDEA 如果不进行配置的话&#xff0c;运行程序时控制台有时候会遇到中文乱码&#xff0c;中文乱码问题非常严重&#xff0c;甚至影响我们对信息的获取和程序的跟踪。开发体验非常不好。 本文中我总结出四点用于解决控制台中文乱码问题的方法&#xff0c;希望有助…...

23岁准备转行嵌入式

前端难找工作&#xff0c;而且感觉没有前景&#xff0c;我觉得我就算入行也不会干多久&#xff0c;软件开发让我感觉不到实在的东西。小时候喜欢去动手制作一些玩具&#xff0c;我对实在一些的东西感兴趣一些例如手表&#xff0c;小机器人等等一些。我有保持坚持学习下去的动力…...

http请求报错:406 Not Acceptable的解决办法

目录 应用场景 基本概念 解决方法 方法一&#xff1a; 方法二&#xff1a; 方法三&#xff1a; 应用场景 接口在返回结果集的时候出现了406的报错&#xff0c;但是返回String类型不会报错&#xff0c;正常返回。 基本概念 406 Not Acceptable是一个HTTP响应状态码&…...

信息化发展75

数字化治理 数字化治理通常指依托互联网、大数据、人工智能等技术和应用&#xff0c;创新社会治理方法与手段&#xff0c;优化社会治理模式&#xff0c;推进社会治理的科学化、精细化、高效化&#xff0c;助力社会治理现代化。数字化治理是数字经济的组成部分之一&#xff0c;…...

C++八股

1、简述一下C中的多态 在面向对象中&#xff0c;多态是指通过基类的指针或引用&#xff0c;在运行时动态调用实际绑定对象函数的行为&#xff0c;与之相对应的编译时绑定函数称为静态绑定。 静态多态 静态多态是编译器在编译期间完成的&#xff0c;编译器会根据实参类型来选择…...

Nat. Commun. | 大规模高分辨单光子成像

本文由论文作者团队(课题组)投稿 单光子雪崩二极管(Single Photon Avalanche Diode,简称SPAD)阵列因其极佳的单光子灵敏度而受到广泛关注,已广泛应用于量子通信与计算、荧光寿命成像、时间飞行成像等各个领域。与同样具有较高灵敏度的EMCCD和sCMOS相比,SPAD阵列能够在极…...

Android开源库

见&#xff1a;GitHub - eHackyd/Android_OpenSourceLibrary: Android开源库的学习笔记...

【小程序 - 基础】页面导航、页面事件、生命周期、WXS脚本_04

目录 一、页面导航 1. 什么是页面导航 2. 小程序中实现页面导航的两种方式 2.1 声明式导航 2.1.1 导航到 tabBar 页面 2.1.2 导航到非 tabBar 页面 2.1.3 后退导航 2.2 编程式导航 2.2.1 导航到 tabBar 页面 2.2.2 导航到非 tabBar 页面 2.2.3 后退导航 2.3. 导航…...

矩阵求导数

矩阵 A ∣ 1 2 1 2 − 1 3 ∣ , 计算 f ( x ) ∣ ∣ A x ∣ ∣ 2 ∣ ∣ x ∣ ∣ 2 的最大值。 矩阵A \begin {vmatrix} 1 & 2 & 1\\2 & -1 & 3 \end {vmatrix},计算f(x) \frac{||Ax||_2}{||x||_2}的最大值。 矩阵A ​12​2−1​13​ ​,计算f(x)∣∣x∣∣2…...

竞赛 大数据疫情分析及可视化系统

文章目录 0 前言2 开发简介3 数据集4 实现技术4.1 系统架构4.2 开发环境4.3 疫情地图4.3.1 填充图(Choropleth maps)4.3.2 气泡图 4.4 全国疫情实时追踪4.6 其他页面 5 关键代码最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 大数据疫…...

数据结构--栈

线性表的定义 前面文章有讲过&#xff0c;线性表就是一次保存单个同类型元素&#xff0c;多个元素之间逻辑上连续 例子&#xff1a;数组&#xff0c;栈&#xff0c;队列&#xff0c;字符串 栈 1.1 栈和队列的特点 栈和队列都是操作受限的线性表。 前面学过的数组&#xff0c;…...

期权定价模型系列【7】:Barone-Adesi-Whaley定价模型

期权定价模型系列第7篇文章 1.前言 目前大连商品交易所、郑州商品交易所、以及上海期货交易所的所有商品期权都为美式期权&#xff0c;并且大商所的所有期权合约会根据BAW(Barone-Adesi-Whaley)美式期权定价模型计算新上市期权合约的挂牌基准价。 BAW模型(Barone-Adesi and W…...

【Axure高保真原型】3D圆柱图_中继器版

今天和大家分享3D圆柱图_中继器版的原型模板&#xff0c;图表在中继器表格里填写具体的数据&#xff0c;调整坐标系后&#xff0c;就可以根据表格数据自动生成对应高度的圆柱图&#xff0c;鼠标移入时&#xff0c;可以查看对应圆柱体的数据……具体效果可以打开下方原型地址体验…...

多个线程启动 ,等待全部执行完毕再搜集数据

前几天在公司的项目上有个同事使用了多线程统计数据&#xff0c;当时出现了一个用户一直使用服务器首次登录信息作为查询信息。找了半天才发现&#xff0c;线程池资源同步了。后面手动将数据set进去的。 等待线程全部执行完毕&#xff0c;这里使用的是减法计数器&#xff0c;也…...

【VIM】VIm-plug插件

如何查找需要的插件 https://github.com/mhinz/vim-startify https://github.com/vim-airline/vim-airline https://github.com/Yggdroot/indentLine github.com/w0ng/vim-hybrid github.com/altercationi/vim-colors-solarized guithub.com/morhetz/gruvbox github.com/sc…...

ssl证书 阿里的域名,腾讯云的证书

目录 1.腾讯云申请ssl免费证书 2.去阿里云进行解析 3.回到腾讯云 4.nginx的配置 说明&#xff1a;阿里云的免费证书用完了&#xff08;每年可以申请20个&#xff09;&#xff0c;还有个项目要用证书&#xff0c;第三方的证书免费的都是90天的。看了下腾讯云业可以申请免费的…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG

TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码&#xff1a;HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...

6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙

Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...

node.js的初步学习

那什么是node.js呢&#xff1f; 和JavaScript又是什么关系呢&#xff1f; node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说&#xff0c; 需要在node.js的环境上进行当JavaScript作为前端开发语言来说&#xff0c;需要在浏览器的环境上进行 Node.js 可…...