基于 STM32 的 NAS私有云盘搭建:集成LwIP 协议、HTTP/HTTPS、WEB前端技术栈(代码示例)
项目概述
在本项目中,我们将搭建一个基于 STM32 的 NAS(网络附加存储)私盘,通过网络访问存储在外部 SATA 硬盘上的文件。该项目将使用 STM32 开发板、外接 SATA 硬盘、LwIP 协议栈以及 FATFS 文件系统来实现文件的上传、下载和管理,用户可以通过简单的 Web 界面进行操作。
系统设计
硬件设计
-
STM32 开发板
- 选择 STM32F407 或 STM32F746 开发板,具备足够的 RAM 和闪存以支持 NAS 功能。
-
存储介质
- 使用外接 SATA 硬盘,通过 SATA 转 USB 适配器连接到 STM32。
-
网络接口
- 选择以太网模块(如 W5500)或 Wi-Fi 模块(如 ESP8266)以实现网络连接。
-
电源管理
- 确保使用的电源适配器能够满足 STM32 开发板和外接硬盘的功耗需求。
软件设计
-
开发环境
- 使用 STM32CubeIDE 或 Keil MDK 作为开发工具。
-
固件库
- 使用 STM32 HAL 库进行硬件抽象和操作。
-
文件系统
- 使用 FATFS 库来管理存储设备上的文件系统,支持文件的读写、删除等操作。
-
网络协议
- 使用 LwIP 协议栈处理 TCP/IP 网络通信。
- 实现 HTTP/HTTPS 协议以支持 Web 服务器功能,支持文件的上传和下载。
配置环境
1. 安装 STM32CubeIDE
- 下载并安装 STM32CubeIDE。
- 按照安装向导完成安装过程。
2. 创建新项目
- 打开 STM32CubeIDE,选择 File -> New -> STM32 Project。
- 在 Board Selector 中选择您的 STM32 开发板(如 STM32F407 或 STM32F746)。
- 配置项目名称和存储路径,点击 Finish。
3. 启用所需的中间件
- 在左侧的 Project Explorer 中,右键单击项目名称,选择 Properties。
- 选择 C/C++ Build -> Settings -> Tool Settings,确保选择了适当的编译工具链。
- 右键单击项目,选择 Manage Embedded Software Packages,然后选择 FATFS 和 LwIP 进行添加和配置。
4. 配置网络接口
- 根据所选的网络模块(以太网或 Wi-Fi),在 Pinout & Configuration 视图中配置相应的引脚和设置。
- 对于以太网模块,请确保启用以太网外设并配置 MAC 地址等参数。
5. 配置外部存储
- 将 SATA 硬盘连接到 STM32 开发板的 USB 接口。
- 在 FATFS 中配置相关参数,以确保可以识别和访问外部存储。
6. 添加库文件
- 导入必要的库文件(如 LwIP 和 FATFS)到项目中,并确保在
main.c或其他相应文件中包含头文件。
STM32代码实现
以下是项目的主要代码实现部分,包括网络初始化、文件操作和 HTTP 服务器的实现。
1. 初始化和网络配置
#include "lwip/init.h"
#include "lwip/netif.h"
#include "lwip/tcpip.h"
#include "ethernetif.h"void init_network(void) {lwip_init(); // 初始化 LwIPstruct netif my_netif;ip_addr_t ipaddr, netmask, gw;// 设置网关和子网IP4_ADDR(&gw, 192, 168, 1, 1); // 网关IP4_ADDR(&ipaddr, 192, 168, 1, 100); // 设备 IPIP4_ADDR(&netmask, 255, 255, 255, 0); // 子网掩码netif_add(&my_netif, &ipaddr, &netmask, &gw, NULL, ethernetif_init, tcpip_input);netif_set_default(&my_netif); // 设置为默认网络接口netif_set_up(&my_netif); // 启动网络接口
}
说明:
lwip_init()初始化 LwIP(轻量级 IP 协议栈)。- 使用
netif_add()添加网络接口,并配置 IP、网关和子网掩码。 netif_set_up()启动网络接口,使其可以进行通信。
2. 文件系统操作
#include "ff.h" // FATFS 头文件void create_file(void) {FATFS fs; // 文件系统对象FIL fil; // 文件对象FRESULT res; // 文件操作结果char buffer[] = "Hello, STM32 NAS!";// 挂载文件系统res = f_mount(&fs, "", 1); if (res == FR_OK) {// 创建文件并写入数据res = f_open(&fil, "test.txt", FA_WRITE | FA_CREATE_ALWAYS); if (res == FR_OK) {f_write(&fil, buffer, sizeof(buffer), NULL); // 写入数据f_close(&fil); // 关闭文件}}
}
说明:
FATFS是文件系统的结构体,FIL是文件对象。- 首先调用
f_mount()挂载文件系统,这样就可以对存储设备进行操作。 - 使用
f_open()创建或打开文件,并使用f_write()写入数据。 - 操作完成后,调用
f_close()关闭文件,以释放资源。
3. HTTP 服务器实现
#include "httpd.h" // HTTP 服务器头文件void start_http_server(void) {httpd_init(); // 初始化 HTTP 服务器
}// HTTP 请求处理示例
static void handle_get_request(struct httpd_state *hs) {const char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello from STM32 NAS!";httpd_send(hs, response, strlen(response)); // 发送响应
}
说明:
httpd_init()初始化 HTTP 服务器,准备接受请求。- 处理 GET 请求的示例函数
handle_get_request()发送一个简单的文本响应。
4. 主函数
int main(void) {// 初始化系统HAL_Init();SystemClock_Config(); // 配置系统时钟MX_GPIO_Init(); // 初始化 GPIOMX_SPI_Init(); // 初始化 SPI(用于以太网模块)MX_USB_OTG_FS_PCD_Init(); // 初始化 USB OTG(用于 SATA 硬盘)init_network(); // 初始化网络create_file(); // 创建文件start_http_server(); // 启动 HTTP 服务器while (1) {// 处理网络事件syscheck_timeouts();}
}
说明:
HAL_Init()初始化硬件抽象层,设置基本的硬件配置。SystemClock_Config()配置系统时钟,以保证系统的正常运行。- 调用
init_network()初始化网络,create_file()创建文件,start_http_server()启动 HTTP 服务器。 - 在
while (1)循环中,不断处理网络事件,保持系统运行。
Web 前端界面交互搭建
1. 项目结构
首先,您需要创建一个简单的项目结构,用于存放前端文件。假设我们将所有的前端文件放在 STM32 的文件系统中的 /www 目录下,项目结构如下:
/www├── index.html├── style.css└── script.js
2. HTML 页面(index.html)
下面是一个简单的 HTML 页面,提供文件上传和下载的功能。
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>STM32 NAS 私盘</title><link rel="stylesheet" href="style.css">
</head>
<body><div class="container"><h1>STM32 NAS 私盘</h1><div class="upload-section"><h2>上传文件</h2><input type="file" id="fileInput"><button id="uploadButton">上传</button></div><div class="file-list"><h2>文件列表</h2><ul id="fileList"></ul></div></div><script src="script.js"></script>
</body>
</html>
3. CSS 样式(style.css)
下面是简单的 CSS 样式,用于美化前端界面。
body {font-family: Arial, sans-serif;background-color: #f4f4f4;margin: 0;padding: 20px;
}.container {max-width: 600px;margin: 0 auto;background: #fff;padding: 20px;border-radius: 5px;box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}h1 {text-align: center;
}.upload-section, .file-list {margin-bottom: 30px;
}button {padding: 10px 15px;background-color: #28a745;color: white;border: none;border-radius: 5px;cursor: pointer;
}button:hover {background-color: #218838;
}
4. JavaScript 代码(script.js)
我们将使用 AJAX 来与 STM32 后端进行交互,实现文件上传和获取文件列表的功能。
document.getElementById('uploadButton').onclick = function() {const fileInput = document.getElementById('fileInput');const file = fileInput.files[0];if (!file) {alert('请选择一个文件!');return;}const formData = new FormData();formData.append('file', file);fetch('/upload', { // 发送文件到后端method: 'POST',body: formData}).then(response => response.text()).then(data => {alert(data);loadFileList(); // 上传成功后更新文件列表}).catch(error => console.error('Error:', error));
};function loadFileList() {fetch('/files') // 请求文件列表.then(response => response.json()).then(files => {const fileList = document.getElementById('fileList');fileList.innerHTML = ''; // 清空文件列表files.forEach(file => {const li = document.createElement('li');li.textContent = file;fileList.appendChild(li);});}).catch(error => console.error('Error:', error));
}// 页面加载时获取文件列表
window.onload = loadFileList;
5. 后端实现
为了使前端能够与后端进行交互,我们需要在 STM32 的 HTTP 服务器中处理文件上传和文件列表请求。
1. 处理文件上传的代码
#include "httpd.h" // HTTP 服务器头文件
#include "ff.h" // FATFS 头文件// 处理文件上传请求
static void handle_file_upload(struct httpd_state *hs) {char buf[512];FIL fil;FRESULT res;char *filename = "uploaded_file.txt"; // 上传的文件名// 读取 HTTP 请求体int bytes_read = httpd_read_request_body(hs, buf, sizeof(buf));if (bytes_read > 0) {// 创建文件并写入数据res = f_open(&fil, filename, FA_WRITE | FA_CREATE_ALWAYS);if (res == FR_OK) {f_write(&fil, buf, bytes_read, NULL); // 写入文件f_close(&fil); // 关闭文件httpd_send_response(hs, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nFile uploaded successfully!"); // 响应成功} else {httpd_send_response(hs, "HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/plain\r\n\r\nFailed to open file!"); // 响应失败}} else {httpd_send_response(hs, "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\n\r\nInvalid request body!"); // 响应无效请求}
}
说明:
- 使用
httpd_read_request_body(hs, buf, sizeof(buf))读取 HTTP 请求体中的文件数据。 - 使用 FATFS API
f_open()创建或打开文件,然后使用f_write()写入数据。 - 根据操作结果,发送不同的 HTTP 响应。
2. 获取文件列表的代码
接下来,我们需要实现一个功能来获取当前存储设备中的所有文件。
#include "ff.h" // FATFS 头文件// 处理文件列表请求
static void handle_file_list(struct httpd_state *hs) {DIR dir;FILINFO fno;FRESULT res;char response[1024];int response_length = 0;// 打开目录res = f_opendir(&dir, "/"); // 假设所有文件都在根目录if (res == FR_OK) {response_length += snprintf(response + response_length, sizeof(response) - response_length, "[");while ((res = f_readdir(&dir, &fno)) == FR_OK && fno.fname[0] != 0) {if (response_length > 1) {response_length += snprintf(response + response_length, sizeof(response) - response_length, ",");}response_length += snprintf(response + response_length, sizeof(response) - response_length, "\"%s\"", fno.fname);}response_length += snprintf(response + response_length, sizeof(response) - response_length, "]");f_closedir(&dir);httpd_send_response(hs, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n");httpd_send_response(hs, response); // 发送文件列表} else {httpd_send_response(hs, "HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/plain\r\n\r\nFailed to open directory!"); // 响应失败}
}
说明:
- 使用
f_opendir()打开根目录,使用f_readdir()读取目录中的文件。 - 将文件名格式化为 JSON 格式的字符串,返回给前端。
- 发送 HTTP 响应,内容类型为
application/json。
3. 绑定 HTTP 请求处理
在 HTTP 服务器初始化时,我们需要将上传和文件列表请求的处理函数绑定到相应的 URL。
void start_http_server(void) {httpd_init(); // 初始化 HTTP 服务器// 绑定请求处理函数httpd_register_uri("/upload", handle_file_upload, HTTP_POST); // 处理文件上传httpd_register_uri("/files", handle_file_list, HTTP_GET); // 获取文件列表
}
说明:
httpd_register_uri()函数用于将请求 URL 与处理函数进行绑定。/uploadURL 用于处理文件上传请求,/filesURL 用于获取文件列表。
项目总结
在本项目中,我们成功地搭建了一个基于 STM32 的 NAS(网络附加存储)私盘,利用 STM32 开发板的强大功能和灵活性,结合 LwIP 协议栈和 FATFS 文件系统,实现了文件的上传、下载和管理。以下是本项目的关键要点和收获:
1. 硬件与软件的结合
通过选择适合的 STM32 开发板(如 STM32F407 或 STM32F746),并配合外部 SATA 硬盘和网络模块(如以太网或 Wi-Fi),我们有效地创建了一个功能强大的 NAS 解决方案。硬件部分的选择对于系统的稳定性和性能至关重要,而软件部分则利用了 STM32 HAL 库、FATFS 文件系统和 LwIP 协议栈,使得硬件功能得以充分发挥。
2. 文件管理功能
我们成功实现了基本的文件管理功能,包括文件的上传、下载和列表展示。通过使用 FATFS 库,我们能够方便地对外接存储进行文件操作,这为用户在 NAS 上进行文件管理提供了便利。
3. Web 前端交互
通过搭建简单的 Web 前端界面,用户能够直观地与 NAS 进行交互。使用 HTML、CSS 和 JavaScript,我们实现了文件上传和文件列表展示功能,使得用户在浏览器中就可以方便地操作存储在 STM32 上的文件。此外,利用 AJAX 技术,我们实现了无刷新数据交互,提升了用户体验。
4. 网络通信实现
使用 LwIP 协议栈,我们为 STM32 开发板实现了网络通信功能。通过 HTTP 协议,前端与后端的交互得以顺利进行。我们实现了对文件上传和文件列表请求的处理,使得用户能够通过网络访问 NAS 中的文件。
相关文章:
基于 STM32 的 NAS私有云盘搭建:集成LwIP 协议、HTTP/HTTPS、WEB前端技术栈(代码示例)
项目概述 在本项目中,我们将搭建一个基于 STM32 的 NAS(网络附加存储)私盘,通过网络访问存储在外部 SATA 硬盘上的文件。该项目将使用 STM32 开发板、外接 SATA 硬盘、LwIP 协议栈以及 FATFS 文件系统来实现文件的上传、下载和管…...
蓝屏?死机?爆CPU?多开卡顿?你有关心过你的硬盘吗?
上来先叠甲 蓝屏、死机、爆cpu、多开卡顿,不一定是硬盘的问题,只是硬盘有问题都可能会引起这些现象,所以不要遇到这些问题就一定认为是硬盘的问题然后说我说的,只是给你一个排除问题的思路。本文会采用比较通俗所以不太专业的角度…...
Flutter开发报错error: unable to unlink old ‘pubspec.yaml‘: Invalid argument
背景:主分支master,然后每人1个分支,每次push到自己分支后,再提mr到master。 于是每次提交前要先git merge origin/master。 有时候会报这个错误,无法merge 原因:很简单,就是pubspec.yaml这个文…...
零基础进程最详解:进程状态、僵尸进程、孤儿进程、阻塞态、挂起态、进程切换、进程常用命令、进程创建、队列优先级
目录 强烈建议全文阅读!!! 强烈建议全文阅读!!! 强烈建议全文阅读!!! 一、进程状态 二、僵尸和孤儿进程 1、僵尸进程 Z(zombie) 2、孤儿进…...
Redis的分布式锁
目录 一、定义与原理 基于Redis的分布式锁 获取锁 释放锁 锁误删问题:因为key值一样,将别人的锁删掉了 锁误判问题二:判断锁和释放锁不是原子性的 Lua脚本 分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁 分布式锁的优点…...
C++笔记---类和对象
1. 类的定义 类是C中的一种自定义类型,是某个具体事物或概念的抽象化代码表示,通过类的成员(变量函数/方法),可以表征出事物或概念的特征。 1.1 类定义的格式 class Stack { public:// 成员函数void Init(int n 4)…...
全国区块链职业技能大赛样题第9套后端源码
后端源码地址:https://blog.csdn.net/Qhx20040819/article/details/140746050 前端源码地址:https://blog.csdn.net/Qhx20040819/article/details/140746216 智能合约+数据库表设计:https://blog.csdn.net/Qhx20040819/article/details/140746646 项目预览 登录 用户管理...
3个功能强大的PDF转换工具,免费试用
给大家分享3个功能强大能满足更高需求的PDF转换工具,都提供免费的试用机会。 1.嗨动PDF编辑器 一款多功能的PDF编辑软件,集PDF阅读、PDF转换、PDF编辑功能为一体。支持转换的格式多样,转换速度快,转换后的排版和内容与原文保持一…...
表单修改数字输入框保留小数点
1.在表单设计打开修改的表单 2.打开需要修改的字段 3.按F12,进入“开发模式” 4.开启“开发者工具”左上角检索工具 开启:鼠标左键点击,当图标颜色为蓝色 关闭:鼠标左键点击,当图标颜色为灰色 5.鼠标移动到需要修改的…...
[VS Code扩展]写一个代码片段管理插件(一):介绍与界面搭建
文章目录 VS Code扩展机制项目搭建创建UI元素活动栏按钮主边栏视图主边栏工具栏按钮侧边栏右键菜单编辑器右键菜单 项目地址 [VS Code扩展]写一个代码片段管理插件(一):介绍与界面搭建[VS Code扩展]写一个代码片段管理插件(二&…...
vxe grid slots 用法
官方例子:Vxe Table v3.8 {field: num1,title: Num1,showHeaderOverflow: true,filters: [{ data: }],editRender: { autofocus: .my-input },slots: {// 使用插槽模板渲染default: num1_default,header: num1_header,footer: num1_footer,filter: num1_filter,edi…...
【网络】基于UDP协议的聊天室(第二篇)
目录 UDP服务器 UDP客户端 在C中,使用UDP协议进行网络通信通常涉及到socket编程。下面我将给出基于UDP的简单的客户端和服务器示例代码。这些示例将使用C标准库以及POSIX套接字接口(主要适用于Linux和类Unix系统)。如果你在使用Windows&…...
【SpringBoot3】场景整合(实战)
0 环境准备 0.0 云服务器 阿里云、腾讯云、华为云 服务器开通; 按量付费,省钱省心 安装以下组件:docker、redis、kafka、prometheus、grafana 下载windterm: https://github.com/kingToolbox/WindTerm/releases/download/2.5…...
【全网最全最详细】MYSQL 面试题大全(上)
目录 一、关系型数据库和非关系型数据库主要有哪些区别? 二、MYSQL的数据存储一定是基于硬盘的吗? 三、InnoDB和MyISAM有什么区别? 四、MyISAM的索引结构是怎么样的?存在的问题是什么? 五、char和varchar的区别? 六、MYSQL 5.x和8.0有什么区别? 七、为什么大厂不…...
【C语言】程序环境,预处理,编译,汇编,链接详细介绍,其中预处理阶段重点讲解
目录 程序环境 翻译环境 1. 翻译环境的两个过程 2. 编译过程的三个阶段 执行环境 预处理(预编译) 1. 预定义符号 2. #define 2.1 用 #define 定义标识符(符号) 2.2 用 #define 定义宏 2.3 #define 的替换规则 2.4 # 和 ## 的用法 2.5 宏和函数 2.6 #undef …...
人生低谷来撸C#--021 多线程
1、概念 线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。 线程是轻量级进程。一个使用线程的常见实例是现代操作系统中…...
【优秀python django系统案例】基于python的医院挂号管理系统,角色包括医生、患者、管理员三种
随着信息技术的迅猛发展,传统的医院挂号管理方式面临着效率低下、排队时间长、信息不对称等诸多问题。这些问题不仅影响患者的就医体验,也加重了医院工作人员的负担。在此背景下,基于Python的医院挂号管理系统应运而生。该系统旨在通过信息化…...
硬盘数据丢失不再怕,四大恢复工具帮你轻松逆转局面!
硬盘故障、误删文件、病毒攻击等原因导致数据丢失的情况时有发生。面对这种情况,如何高效、快速地进行硬盘数据恢复呢?接下来几款好用的数据恢复软件推荐给大家。 一、福昕数据恢复:全方位恢复,让数据无遗漏 链接:ww…...
自定义封装日历组件
自定义日历 工作需要,但现有框架封装的日历无法满足需求,又找不到更好的插件,所以准备自己封装一个。 效果图和说明 一个很简易版的demo日历,本文只提供最基本的功能代码,便于阅读二开。 新建calendar.vue文件 <…...
【大模型】【面试】独家总结表格
问题解答你能解释一下Transformer架构及其在大型语言模型中的作用吗?Transformer架构是一种深度神经网络架构,于2017年由Vaswani等人在他们的论文“Attention is All You Need”中首次提出。自那以后,它已成为大型语言模型(如BERT和GPT)最常用的架构。 Transformer架构使用…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
