首页
留言
导航
统计
Search
1
追番推荐!免费看动漫的网站 - 支持在线观看和磁力下载
3,139 阅读
2
推荐31个docker应用,每一个都很实用
1,501 阅读
3
PVE自动启动 虚拟机 | 容器 顺序设置及参数说明
1,050 阅读
4
一条命令,永久激活!Office 2024!
679 阅读
5
优选 Cloudflare 官方 / 中转 IP
538 阅读
默认分类
服务器
宝塔
VPS
Docker
OpenWRT
Nginx
群晖
前端编程
Vue
React
Angular
NodeJS
uni-app
后端编程
Java
Python
SpringBoot
SpringCloud
流程引擎
检索引擎
Linux
CentOS
Ubuntu
Debian
数据库
Redis
MySQL
Oracle
虚拟机
VMware
VirtualBox
PVE
Hyper-V
计算机
网络技术
网站源码
主题模板
登录
Search
标签搜索
Java
小程序
Redis
SpringBoot
docker
Typecho
Cloudflare
uni-app
docker部署
虚拟机
WordPress
群晖
CentOS
Vue
Java类库
Linux命令
防火墙配置
Mysql
脚本
Nginx
微醺
累计撰写
266
篇文章
累计收到
11
条评论
首页
栏目
默认分类
服务器
宝塔
VPS
Docker
OpenWRT
Nginx
群晖
前端编程
Vue
React
Angular
NodeJS
uni-app
后端编程
Java
Python
SpringBoot
SpringCloud
流程引擎
检索引擎
Linux
CentOS
Ubuntu
Debian
数据库
Redis
MySQL
Oracle
虚拟机
VMware
VirtualBox
PVE
Hyper-V
计算机
网络技术
网站源码
主题模板
页面
留言
导航
统计
搜索到
266
篇与
的结果
2023-07-11
推荐一款CMS内容管理系统,完全开源、免费,真正实现“0”代码建站!
正文我今天,推荐一个系统项目。第一次使用就有点上头,爱不释手,必须要推荐给大家。这是我目前见过最好的系统项目。功能完整,代码结构清晰。值得推荐。📚项目介绍🔥本项目系统是一款梦想家内容发布系统采用流行的SpringBoot搭建,支持静态化、标签化建站。不需要专业的后台开发技能,会HTML就能建站,上手超简单;只需使用系统提供的标签就能轻松建设网站。全面支持各类表单字段,真正实现“0”代码建网站。特点免费开源:基于APACHE 2.0开源协议,源代码完全开源;标签建站:不需要专业的后台开发技能,只要使用系统提供的标签,就能轻松建设网站;开发方便:支持在线上传模版包开发方便快捷;零代码量:真正实现“0”代码建站,后台代码一点都不需要动;每月更新:每月进行系统升级,分享更多好用的模版与插件。面向对象政府:可以使用Dreamer CMS来快速构建政府门户;电信:可以使用Dreamer CMS来快速构建电信综合门户;企业:可以使用Dreamer CMS构建信息门户,知识管理平台,也可作为基础技术框架,是企业在创立初期很好的技术选型;个人开发者:可以使用Dreamer CMS承接外包项目;技术框架核心框架:Spring Boot 2安全框架:Apache Shiro 1.9.1工具包:Hutool 5.8.5持久层框架:MyBatis 2.2.2日志管理:Logback模版框架:ThymeleafJS框架:jQuery,BootstrapCSS框架:Bootstrap富文本:Ueditor、editor.md开发环境建议开发者使用以下环境,这样避免版本带来的问题JDK:Jdk8IDE:Spring Tool Suite 4(STS)或 IntelliJ IDEADB:Mysql 5.7,Windows配置安装Mysql5.7,请参考:https://www.iteachyou.cc/article/a1db138b4a89402ab50f3499edeb30c2Redis:3.2+,Windows配置安装Redis教程,请参考:https://www.iteachyou.cc/article/4b0a638f65fa4fb1b9644cf461dba602LomBok 项目需要使用Lombok支持,Lombok安装教程,请参考:https://www.iteachyou.cc/article/55ec2939c29147eca5bebabf19621655系统结构快速入门CMS包括两个部分(代码部分、资源部分)代码不多说。资源就是图片、模版等,该目录在application.yml中web.resource-path配置项目中配置。视频教程:Dreamer CMS后台使用教程:https://www.iteachyou.cc/list-6s3bg7tf/dreamercms/1/10Dreamer CMS模版开发教程:https://www.iteachyou.cc/list-l54xs53b/tempdev/1/10百度网盘下载链接:https://pan.baidu.com/s/16nLVa44OkloL8sTpW6e2QQ 提取码:2c8i 在线观看视频地址:https://space.bilibili.com/482273402克隆项目到本地工作空间导入Eclipse或Sts等开发工具(推荐使用Spring Tools Suite 4),项目需要使用Lombok支持,Lombok安装教程,请参考https://www.iteachyou.cc/article/55ec2939c29147eca5bebabf19621655项目需要Redis,请自行修改application.yml中Redis配置修改项目资源目录,application.yml文件web.resource-path配置项(如D:/dreamer-cms/)导入数据库src/main/resources/db/db.sql,要求Mysql5.7版本,并修改application-(dev|prd).yml中数据配置将项目src/main/resources/db/dreamer-cms.zip文件解压,保证解压后的目录路径的名称和资源目录一致运行项目DreamerCMSApplication.java网站首页:https://localhost:8888 项目管理后台:https://localhost:8888/admin管理后台用户名:wangjn;密码:123456模版标签开发教程请参考:http://doc.iteachyou.cc系统美图地址项目地址:https://gitee.com/iteachyou/dreamer_cms梦想家CMS官网:http://cms.iteachyou.cc梦想家CMS管理后台:http://cms.iteachyou.cc/admin演示账号:demo1演示密码:123456管理员:wangjn管理员密码:123456
2023年07月11日
33 阅读
0 评论
0 点赞
2023-07-08
常见内网穿透工具,收好了!
前言本文以渗透的视角,总结几种个人常用的内网穿透,内网代理工具,介绍其简单原理和使用方法。1. nps-npc1.1 简介nps 是一款轻量级、高性能、功能强大的内网穿透代理服务器。目前支持 tcp、udp 流量转发,可支持任何 tcp、udp上层协议 (访问内网网站、本地支付接口调试、ssh访问、远程桌面,内网dns解析等等……),此外还支持内网http代理、内网socks5代理、p2p等,并带有功能强大的web管理端。一台有公网IP的服务器(VPS)运行服务端(NPS)一个或多个运行在内网的服务器或者PC运行客户端(NPC)1.2 特点Go语言编写支持跨平台支持多种协议的代理web管理端1.3 使用方法https://github.com/ehang-io/nps/releases1.4 NPS安装配置 找到自己服务器相应版本的server:cd ~ wget https://github.com/cnlh/nps/releases/download/v0.23.2/linux_amd64_server.tar.gz tar xzvf linux_amd64_server.tar.gz cd ~/nps 在nps目录下面会有一个nps可执行文件、conf配置目录和web网页目录,我们只需要修改 conf/nps.conf 即可:vim conf/nps.conf 需要改一下 #web 下面的几个参数,web_host= 服务器IP或者域名 web_username= admin(登录用户名) web_password= 你的密码 web_port=8080(web管理端口) 修改 #bridge 可以更改 NPC 的连接端口。比如我们拿到一台权限受限的服务器,有防火墙,可能只有部分端口(80,443)可以出网,就需要修改成出网端口。##bridge bridge_type=tcp bridge_port=443 # 修改连接端口 bridge_ip=0.0.0.0 启动#Mac/Linux ./nps test|start|stop|restart|status 测试配置文件|启动|停止|重启|状态 #Windows nps.exe test|start|stop|restart|status 测试配置文件|启动|停止|重启|状态 1.5 NPC./npc -server=你的IP:8024 -vkey=唯一验证密码 -type=tcp 新建好客户端后,也可以在 + 中看到,详细的客户端连接命令:1.6 web管理端在客户端界面可以通过新增的方式添加客户端连接,每一个连接的vkey都是唯一区分的。每一个客户端,在建立连接后,都可以建立多个不同协议的隧道,这一个个隧道就是不同的代理了。通过不同的协议和端口就可以连接代理的内网机器。2. frp2.1 简介frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。2.2 特点客户端服务端通信支持 TCP、KCP 以及 Websocket 等多种协议。端口复用,多个服务通过同一个服务端端口暴露。跨平台,但是支持的比nps少一点多种插件,提供很多功能2.3 使用方法下载:https://github.com/fatedier/frp/releases以下内容摘自:https://segmentfault.com/a/11900000218768361). 通过 rdp 访问家里的机器修改 frps.ini 文件,为了安全起见,这里最好配置一下身份验证,服务端和客户端的 common 配置中的 token 参数一致则身份验证通过:# frps.ini [common] bind_port = 7000 # 用于身份验证,请自行修改,要保证服务端与客户端一致 token = abcdefgh启动 frps:./frps -c ./frps.ini修改 frpc.ini 文件,假设 frps 所在服务器的公网 IP 为 x.x.x.x:# frpc.ini [common] server_addr = x.x.x.x server_port = 7000 # 用于身份验证,请自行修改,要保证服务端与客户端一致 token = abcdefgh [rdp] type = tcp local_ip = 127.0.0.1 local_port = 3389 remote_port = 6000 启动 frpc:./frpc -c ./frpc.ini通过 rdp 访问远程的机器,地址为:x.x.x.x:6000开机自启 针对 Windows 系统,为了便于使用,可以配置一下开机的时候静默启动。在 frpc.exe 的同级目录创建一个 start_frpc.vbs'start_frpc.vbs '请根据实际情况修改路径 CreateObject("WScript.Shell").Run """D:\Program Files\frp_windows_amd64\frpc.exe""" & "-c" &"""D:\Program Files\frp_windows_amd64\frpc.ini""",0 复制 start_frpc.vbs 文件,打开以下目录,注意将 <USER_NAME> 改为你的用户名C:\Users\<USER_NAME>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup 鼠标右击,粘贴为快捷方式即可。 2). 通过 SSH 访问公司内网机器frps 的部署步骤同上。启动 frpc,配置如下:# frpc.ini [common] server_addr = x.x.x.x server_port = 7000 # 用于身份验证,请自行修改,要保证服务端与客户端一致 token = abcdefgh [ssh] type = tcp local_ip = 127.0.0.1 local_port = 22 remote_port = 6000 通过 SSH 访问内网机器,假设用户名为 testssh -oPort=6000 test@x.x.x.x3). 通过自定义域名访问部署于内网的 Web 服务有时想要让其他人通过域名访问或者测试我们在本地搭建的 Web 服务,但是由于本地机器没有公网 IP,无法将域名解析到本地的机器,通过 frp 就可以实现这一功能,以下示例为 http 服务,https 服务配置方法相同, vhost_http_port 替换为 vhost_https_port, type 设置为 https 即可。修改 frps.ini 文件,设置 http 访问端口为 8080:# frps.ini [common] bind_port = 7000 vhost_http_port = 8080 # 用于身份验证,请自行修改,要保证服务端与客户端一致 token = abcdefgh 启动 frps:./frps -c ./frps.ini修改 frpc.ini 文件,假设 frps 所在的服务器的 IP 为 x.x.x.x,local_port 为本地机器上 Web 服务对应的端口,绑定自定义域名 www.yourdomain.com:# frpc.ini [common] server_addr = x.x.x.x server_port = 7000 # 用于身份验证,请自行修改,要保证服务端与客户端一致 token = abcdefgh [web] type = http local_port = 80 custom_domains = www.yourdomain.com 启动 frpc./frpc -c ./frpc.ini将 www.yourdomain.com 的域名 A 记录解析到 IP x.x.x.x,如果服务器已经有对应的域名,也可以将 CNAME 记录解析到服务器原先的域名。通过浏览器访问 http://www.yourdomain.com:8080 即可访问到处于内网机器上的 Web 服务。4). 对外提供简单的文件访问服务通过 static_file 插件可以对外提供一个简单的基于 HTTP 的文件访问服务。frps 的部署步骤同上。启动 frpc,启用 static_file 插件,配置如下:# frpc.ini [common] server_addr = x.x.x.x server_port = 7000 # 用于身份验证,请自行修改,要保证服务端与客户端一致 token = abcdefgh [test_static_file] type = tcp remote_port = 6000 plugin = static_file # 要对外暴露的文件目录 plugin_local_path = /tmp/file # 访问 url 中会被去除的前缀,保留的内容即为要访问的文件路径 plugin_strip_prefix = static plugin_http_user = abc plugin_http_passwd = abc 通过浏览器访问 http://x.x.x.x:6000/static/ 来查看位于 /tmp/file 目录下的文件,会要求输入已设置好的用户名和密码。常用功能 (1)统计面板通过浏览器查看 frp 的状态以及代理统计信息展示。注:Dashboard 尚未针对大量的 proxy 数据展示做优化,如果出现 Dashboard 访问较慢的情况,请不要启用此功能。需要在 frps.ini 中指定 dashboard 服务使用的端口,即可开启此功能:[common] dashboard_port = 7500 # dashboard 用户名密码,默认都为 admin dashboard_user = admin dashboard_pwd = admin 打开浏览器通过 http://[server_addr]:7500 访问 dashboard 界面,用户名密码默认为 admin。(2)加密与压缩这两个功能默认是不开启的,需要在 frpc.ini 中通过配置来为指定的代理启用加密与压缩的功能,压缩算法使用 snappy :# frpc.ini [ssh] type = tcp local_port = 22 remote_port = 6000 use_encryption = true use_compression = true 如果公司内网防火墙对外网访问进行了流量识别与屏蔽,例如禁止了 SSH 协议等,通过设置 use_encryption = true,将 frpc 与 frps 之间的通信内容加密传输,将会有效防止流量被拦截。如果传输的报文长度较长,通过设置 use_compression = true 对传输内容进行压缩,可以有效减小 frpc 与 frps 之间的网络流量,加快流量转发速度,但是会额外消耗一些 CPU 资源。(3)TLS从 v0.25.0 版本开始 frpc 和 frps 之间支持通过 TLS 协议加密传输。通过在 frpc.ini 的 common 中配置 tls_enable = true 来启用此功能,安全性更高。为了端口复用,frp 建立 TLS 连接的第一个字节为 0x17。注意:启用此功能后除 xtcp 外,不需要再设置 use_encryption。 (4)代理限速目前支持在客户端的代理配置中设置代理级别的限速,限制单个 proxy 可以占用的带宽。# frpc.ini [ssh] type = tcp local_port = 22 remote_port = 6000 bandwidth_limit = 1MB 在代理配置中增加 bandwidth_limit 字段启用此功能,目前仅支持 MB 和 KB 单位。(5)范围端口映射在 frpc 的配置文件中可以指定映射多个端口,目前只支持 tcp 和 udp 的类型。这一功能通过 range: 段落标记来实现,客户端会解析这个标记中的配置,将其拆分成多个 proxy,每一个 proxy 以数字为后缀命名。例如要映射本地 6000-6005, 6007 这 6 个端口,主要配置如下:# frpc.ini [range:test_tcp] type = tcp local_ip = 127.0.0.1 local_port = 6000-6006,6007 remote_port = 6000-6006,6007 实际连接成功后会创建 8 个 proxy,命名为 test_tcp_0, test_tcp_1 ... test_tcp_7。3. ngrok3.1 简介ngrok 是一个反向代理,通过在公共端点和本地运行的 Web 服务器之间建立一个安全的通道,实现内网主机的服务可以暴露给外网。ngrok 可捕获和分析所有通道上的流量,便于后期分析和重放,所以ngrok可以很方便地协助服务端程序测试。4.2 特点官方维护,一般较为稳定跨平台,闭源有流量记录和重发功能4.3 使用方法进入ngrok官网(https://ngrok.com/),注册ngrok账号并下载ngrok;根据官网给定的授权码,运行如下授权命令;./ngrok authtoken 1hAotxhmORtzCYvUc3BsxDBPh1H_**./ngrok http 80即可将机器的80端口http服务暴露到公网,并且会提供一个公网域名。可以通过官网的UI界面查看数据包和流量等等(但是要付费==、)还可以通过一些命令将内网的文件和其他TCP服务 暴露到公网中。有授权的设置文件共享ngrok http -auth="user:password" file:///Users/alan/share 无授权的设置文件共享ngrok http "file:///C:\Users\alan\Public Folder" 将主机的3389的TCP端口暴露到公网ngrok tcp 3389 更多使用方法参考:https://ngrok.com/docs
2023年07月08日
30 阅读
0 评论
0 点赞
2023-07-07
再见了 Xshell、iTerm2、FinalShell,这款开源的终端工具真香
现目前的的远程终端工具有很多,功能齐全好用的收费,免费的功能外观又不怎么满意。XShell 收费而且感觉用起来也一般,putty 免费但很不方便,我们的 FinalShell 好用、功能齐全可以说是一款非常好的终端工具但外观总是觉得不尽人意。直到我发现这款「Tabby」。引言Tabby 是一个高度可配置的终端模拟器、SSH 和串行客户端,适用于 Windows、macOS 和 Linux。特性集成的 SSH 和 Telnet 客户端和连接管理器集成串口终端主题和配色方案完全可配置的快捷键和多和弦快捷键拆分窗格记住你的标签PowerShell(和 PS Core)、WSL、Git-Bash、Cygwin、MSYS2、Cmder 和 CMD 支持通过 Zmodem 从/到 SSH 会话的直接文件传输完整的 Unicode 支持,包括双角字符不会因快速流动的输出而窒息Windows 上的正确 shell 体验,包括选项卡完成(通过 Clink)用于 SSH 机密和配置的集成加密容器SSH、SFTP 和 Telnet 客户端可用作 Web 应用程序 (也可自托管)。地址GitHub:https://github.com/Eugeny/tabby官网:https://tabby.sh/在线体验:https://tabby.sh/app 大家可以先在线体验,这个还挺厉害的功能介绍默认打开是本地终端,如果对本地终端工具不满意也可以试试它创建远程连接点击右上角齿轮点击左侧菜单的 Profiles&connections点击蓝色按钮 New profile 添加新的远程连接选择第一个,创建 SSH 连接填入服务器信息等,保存连接点击窗口按钮选中你创建的远程连接支持FTP 没错他直接支持文件的上传和下载总结Tabby 市面上少见的既好用又好看还免费开源的远程终端工具。
2023年07月07日
63 阅读
0 评论
0 点赞
2023-07-06
利用docker搭建home-assistant打造全屋智能
home-assistant 安装打开注册表搜索 homeassistant/home-assistant 下载 docker映像 下载完成后启动网络类型一定 不要选择bridge ,选择 bridge-host 或者 host 否则未在同一局域网 homekit 无法绑定选择高级-添加环境变量-保存TZ:Asia/Shanghai端口本地 8321 - 容器 8321,如果选择的 host 则不需要设置端口添加一个文件夹并装载到 /config,完成并运行容器Home Assistant 安装 HACS什么是 HACS?HACS 可以理解为 Home Assistant 上的一个第三方应用商店打开容器详情页面-选择终端-新建命令右键粘贴并运行以下命令wget -O - https://get.hacs.xyz | bash -运行完成后重启容器浏览器打开并输入 群晖ip + 端口8123 并访问根据向导完成并创建自己的账号点击「配置」,点击「集成」,点击右下角的「添加集成」,在搜索框里输入「HACS」并点击,接受协议。需要全选,无法不选。点击「提交」,设备注册。需要先拥有Github账号,没有的话注册一个并在浏览器上登录。打开 HACS商店 浏览并下载存储库:Xiaomi Miot Auto,下载完成后重启容器重新登录后打开 配置-设备与服务-添加集成-搜索xiaomi-安装Xiaomi Miot Auto-选择账号集成模式配置 HomeKit 后打开通知手机扫描二维码后即可使用iphone控制小米设备
2023年07月06日
30 阅读
0 评论
0 点赞
2023-07-06
解决WordPress开启CDN后评论IP不正确的方法
前言最近启用CDN内容分发后发现评论评论人的IP都是CDN的节点IP(也有可能是服务器IP),由于使用CDN后访问用户服务器的IP都变成CDN服务器的IP。百度一下,发现了一个好的方法。只要添加一段代码,IP地址就可以正常了,其实CDN在头部应该有把访问者IP发送的,那段代码就是把 wordpress 的 ip 中变量换成 $_SERVER 获取的。这个可以用在任意CDN和反代里面,IP地址可以获取正确的。在 WordPress 安装根目录 下面 wp-config.php 文件,打开后,在其内添加下面代码:if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $list = explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']); $_SERVER['REMOTE_ADDR'] = $list[0]; }
2023年07月06日
73 阅读
0 评论
0 点赞
2023-07-03
SpringBoot 服务接口限流,搞定!
前言在开发高并发系统时有三把利器用来保护系统: 缓存 、降级 和 限流 。限流 可以认为服务降级的一种,限流通过限制请求的流量以达到保护系统的目的。一般来说,系统的吞吐量是可以计算出一个阈值的,为了保证系统的稳定运行,一旦达到这个阈值,就需要限制流量并采取一些措施以完成限制流量的目的。比如:延迟处理,拒绝处理,或者部分拒绝处理等等。否则,很容易导致服务器的宕机。常见限流算法计数器限流计数器限流算法 是最为简单粗暴的解决方案,主要用来限制总并发数,比如数据库连接池大小、线程池大小、接口访问并发数等都是使用计数器算法。如:使用 AomicInteger 来进行统计当前正在并发执行的次数,如果超过域值就直接拒绝请求,提示系统繁忙。漏桶算法漏桶算法 思路很简单,我们把水比作是 请求,漏桶比作是系统处理能力极限,水先进入到漏桶里,漏桶里的水按一定速率流出,当流出的速率小于流入的速率时,由于漏桶容量有限,后续进入的水直接溢出(拒绝请求),以此实现限流。令牌桶算法令牌桶算法 的原理也比较简单,我们可以理解成医院的挂号看病,只有拿到号以后才可以进行诊病。系统会维护一个令牌(token)桶,以一个恒定的速度往桶里放入令牌(token),这时如果有请求进来想要被处理,则需要先从桶里获取一个令牌(token),当桶里没有令牌(token)可取时,则该请求将被拒绝服务。令牌桶算法通过控制桶的容量、发放令牌的速率,来达到对请求的限制。单机模式Google 开源工具包 Guava 提供了限流工具类 RateLimiter ,该类基于 令牌桶算法 实现流量限制,使用十分方便,而且十分高效引入依赖到 pom.xml<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1-jre</version> </dependency>创建注解 Limit package com.example.demo.common.annotation; import java.lang.annotation.*; import java.util.concurrent.TimeUnit; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Documented public @interface Limit { // 资源key String key() default ""; // 最多访问次数 double permitsPerSecond(); // 时间 long timeout(); // 时间类型 TimeUnit timeunit() default TimeUnit.MILLISECONDS; // 提示信息 String msg() default "系统繁忙,请稍后再试"; }注解 AOP 实现package com.example.demo.common.aspect; import com.example.demo.common.annotation.Limit; import com.example.demo.common.dto.R; import com.example.demo.common.exception.LimitException; import com.google.common.collect.Maps; import com.google.common.util.concurrent.RateLimiter; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.Map; @Slf4j @Aspect @Component public class LimitAspect { private final Map<String, RateLimiter> limitMap = Maps.newConcurrentMap(); @Around("@annotation(com.example.demo.common.annotation.Limit)") public Object around(ProceedingJoinPoint pjp) throws Throwable { MethodSignature signature = (MethodSignature)pjp.getSignature(); Method method = signature.getMethod(); //拿limit的注解 Limit limit = method.getAnnotation(Limit.class); if (limit != null) { //key作用:不同的接口,不同的流量控制 String key=limit.key(); RateLimiter rateLimiter; //验证缓存是否有命中key if (!limitMap.containsKey(key)) { // 创建令牌桶 rateLimiter = RateLimiter.create(limit.permitsPerSecond()); limitMap.put(key, rateLimiter); log.info("新建了令牌桶={},容量={}",key,limit.permitsPerSecond()); } rateLimiter = limitMap.get(key); // 拿令牌 boolean acquire = rateLimiter.tryAcquire(limit.timeout(), limit.timeunit()); // 拿不到命令,直接返回异常提示 if (!acquire) { log.debug("令牌桶={},获取令牌失败",key); throw new LimitException(limit.msg()); } } return pjp.proceed(); } }注解使用permitsPerSecond 代表请求总数量timeout 代表限制时间即 timeout 时间内,只允许有 permitsPerSecond 个请求总数量访问,超过的将被限制不能访问package com.example.demo.module.test; import com.example.demo.common.annotation.Limit; import com.example.demo.common.dto.R; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; @Slf4j @RestController public class TestController { @Limit(key = "cachingTest", permitsPerSecond = 1, timeout = 500, msg = "当前排队人数较多,请稍后再试!") @GetMapping("cachingTest") public R cachingTest(){ log.info("------读取本地------"); List<String> list = new ArrayList<>(); list.add("蜡笔小新"); list.add("哆啦A梦"); list.add("四驱兄弟"); return R.ok(list); } }测试启动项目,快读刷新访问 /cachingTest 请求可以看到访问已经有被成功限制该种方式属于 应用级限流 ,假设将应用部署到多台机器,应用级限流方式只是单应用内的请求限流,不能进行全局限流。因此我们需要分布式限流和接入层限流来解决这个问题。分布式模式基于 redis + lua 脚本的 分布式限流 分布式限流 最关键的是要将限流服务做成原子化,而解决方案可以使用 redis + lua 或者 nginx + lua 技术进行实现,通过这两种技术可以实现的 高并发 和 高性能 。首先我们来使用 redis + lua 实现时间窗内某个接口的请求数限流,实现了该功能后可以改造为限流总并发/请求数和限制总资源数。lua 本身就是一种编程语言,也可以使用它实现复杂的令牌桶或漏桶算法。 因操作是在一个 lua 脚本中(相当于原子操作),又因 redis 是单线程模型,因此是线程安全的。相比 redis 事务来说,lua 脚本有以下优点减少网络开销 :不使用 lua 的代码需要向 redis 发送多次请求,而脚本只需一次即可,减少网络传输;原子操作 :redis 将整个脚本作为一个原子执行,无需担心并发,也就无需事务;复用 :脚本会永久保存 redis 中,其他客户端可继续使用。创建注解 RedisLimitpackage com.example.demo.common.annotation; import com.example.demo.common.enums.LimitType; import java.lang.annotation.*; @Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface RedisLimit { // 资源名称 String name() default ""; // 资源key String key() default ""; // 前缀 String prefix() default ""; // 时间 int period(); // 最多访问次数 int count(); // 类型 LimitType limitType() default LimitType.CUSTOMER; // 提示信息 String msg() default "系统繁忙,请稍后再试"; }注解 AOP 实现package com.example.demo.common.aspect; import com.example.demo.common.annotation.RedisLimit; import com.example.demo.common.enums.LimitType; import com.example.demo.common.exception.LimitException; import com.google.common.collect.ImmutableList; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Objects; @Slf4j @Aspect @Configuration public class RedisLimitAspect { private final RedisTemplate<String, Object> redisTemplate; public RedisLimitAspect(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; } @Around("@annotation(com.example.demo.common.annotation.RedisLimit)") public Object around(ProceedingJoinPoint pjp){ MethodSignature methodSignature = (MethodSignature)pjp.getSignature(); Method method = methodSignature.getMethod(); RedisLimit annotation = method.getAnnotation(RedisLimit.class); LimitType limitType = annotation.limitType(); String name = annotation.name(); String key; int period = annotation.period(); int count = annotation.count(); switch (limitType){ case IP: key = getIpAddress(); break; case CUSTOMER: key = annotation.key(); break; default: key = StringUtils.upperCase(method.getName()); } ImmutableList<String> keys = ImmutableList.of(StringUtils.join(annotation.prefix(), key)); try { String luaScript = buildLuaScript(); DefaultRedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class); Number number = redisTemplate.execute(redisScript, keys, count, period); log.info("Access try count is {} for name = {} and key = {}", number, name, key); if(number != null && number.intValue() == 1){ return pjp.proceed(); } throw new LimitException(annotation.msg()); }catch (Throwable e){ if(e instanceof LimitException){ log.debug("令牌桶={},获取令牌失败",key); throw new LimitException(e.getLocalizedMessage()); } e.printStackTrace(); throw new RuntimeException("服务器异常"); } } public String buildLuaScript(){ return "redis.replicate_commands(); local listLen,time" + "\nlistLen = redis.call('LLEN', KEYS[1])" + // 不超过最大值,则直接写入时间 "\nif listLen and tonumber(listLen) < tonumber(ARGV[1]) then" + "\nlocal a = redis.call('TIME');" + "\nredis.call('LPUSH', KEYS[1], a[1]*1000000+a[2])" + "\nelse" + // 取出现存的最早的那个时间,和当前时间比较,看是小于时间间隔 "\ntime = redis.call('LINDEX', KEYS[1], -1)" + "\nlocal a = redis.call('TIME');" + "\nif a[1]*1000000+a[2] - time < tonumber(ARGV[2])*1000000 then" + // 访问频率超过了限制,返回0表示失败 "\nreturn 0;" + "\nelse" + "\nredis.call('LPUSH', KEYS[1], a[1]*1000000+a[2])" + "\nredis.call('LTRIM', KEYS[1], 0, tonumber(ARGV[1])-1)" + "\nend" + "\nend" + "\nreturn 1;"; } public String getIpAddress(){ HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); String ip = request.getHeader("x-forwarded-for"); if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ ip = request.getHeader("Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ ip = request.getHeader("WL-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ ip = request.getRemoteAddr(); } return ip; } }注解使用count 代表请求总数量period 代表限制时间即 period 时间内,只允许有 count 个请求总数量访问,超过的将被限制不能访问package com.example.demo.module.test; import com.example.demo.common.annotation.Limit; import com.example.demo.common.annotation.RedisLimit; import com.example.demo.common.dto.R; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; @Slf4j @RestController public class TestController { @RedisLimit(key = "cachingTest", count = 2, period = 2, msg = "当前排队人数较多,请稍后再试!") // @Limit(key = "cachingTest", permitsPerSecond = 1, timeout = 500, msg = "当前排队人数较多,请稍后再试!") @GetMapping("cachingTest") public R cachingTest(){ log.info("------读取本地------"); List<String> list = new ArrayList<>(); list.add("蜡笔小新"); list.add("哆啦A梦"); list.add("四驱兄弟"); return R.ok(list); } }测试启动项目,快读刷新访问 /cachingTest 请求可以看到访问已经有被成功限制这只是其中一种实现方式,尚有许多实现方案,经供参考。
2023年07月03日
18 阅读
0 评论
0 点赞
2023-07-03
SpringBoot 实现 PDF 添加水印有哪些方案?
简介PDF(Portable Document Format,便携式文档格式) 是一种流行的文件格式,它可以在多个操作系统和应用程序中进行查看和打印。在某些情况下,我们需要对 PDF 文件添加水印,以使其更具有辨识度或者保护其版权。本文将介绍如何使用 Spring Boot 来实现 PDF 添加水印的方式。方式一:使用 Apache PDFBox 库PDFBox 是一个流行的、免费的、用 Java 编写的库,它可以用来创建、修改和提取 PDF内容。 PDFBox 提供了许多 API,包括添加文本水印的功能。{mtitle title="添加 PDFBox "/}首先,在 pom.xml 文件中添加 PDFBox 的依赖:<dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.24</version> </dependency>{mtitle title="添加水印"/}在添加水印之前,需要读取 原始PDF 文件:PDDocument document = PDDocument.load(new File("original.pdf"));然后,遍历 PDF 中的所有页面,并使用 PDPageContentStream 添加水印:// 遍历 PDF 中的所有页面 for (int i = 0; i < document.getNumberOfPages(); i++) { PDPage page = document.getPage(i); PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true); // 设置字体和字号 contentStream.setFont(PDType1Font.HELVETICA_BOLD, 36); // 设置透明度 contentStream.setNonStrokingColor(200, 200, 200); // 添加文本水印 contentStream.beginText(); contentStream.newLineAtOffset(100, 100); // 设置水印位置 contentStream.showText("Watermark"); // 设置水印内容 contentStream.endText(); contentStream.close(); }最后,需要保存修改后的 PDF 文件:document.save(new File("output.pdf")); document.close();{mtitle title="完整代码"/}下面是使用 PDFBox 来实现 PDF 添加水印的完整代码:import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.font.PDType1Font; import java.io.File; import java.io.IOException; public class PdfBoxWatermark { public static void main(String[] args) throws IOException { // 读取原始 PDF 文件 PDDocument document = PDDocument.load(new File("original.pdf")); // 遍历 PDF 中的所有页面 for (int i = 0; i < document.getNumberOfPages(); i++) { PDPage page = document.getPage(i); PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true); // 设置字体和字号 contentStream.setFont(PDType1Font.HELVETICA_BOLD, 36); // 设置透明度 contentStream.setNonStrokingColor(200, 200, 200); // 添加文本水印 contentStream.beginText(); contentStream.newLineAtOffset(100, 100); // 设置水印位置 contentStream.showText("Watermark"); // 设置水印内容 contentStream.endText(); contentStream.close(); } // 保存修改后的 PDF 文件 document.save(new File("output.pdf")); document.close(); } }方式二:使用 iText 库iText 是一款流行的 Java PDF 库,它可以用来创建、读取、修改和提取 PDF 内容。iText提供了许多API,包括添加文本水印的功能。{mtitle title="添加iText依赖"/}在 pom.xml 文件中添加 iText 的依赖:<dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13</version> </dependency>{mtitle title="添加水印"/}在添加水印之前,需要读取原始 PDF 文件:PdfReader reader = new PdfReader("original.pdf"); PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("output.pdf"));然后,遍历 PDF 中的所有页面,并使用 PdfContentByte 添加水印:// 获取 PDF 中的页数 int pageCount = reader.getNumberOfPages(); // 添加水印 for (int i = 1; i <= pageCount; i++) { PdfContentByte contentByte = stamper.getUnderContent(i); // 或者 getOverContent() contentByte.beginText(); contentByte.setFontAndSize(BaseFont.createFont(), 36f); contentByte.setColorFill(BaseColor.LIGHT_GRAY); contentByte.showTextAligned(Element.ALIGN_CENTER, "Watermark", 300, 400, 45); contentByte.endText(); }最后,需要保存修改后的 PDF 文件并关闭文件流:stamper.close(); reader.close();{mtitle title="完整代码"/}下面是使用 iText 来实现 PDF 添加水印的完整代码:import com.itextpdf.text.*; import com.itextpdf.text.pdf.*; import java.io.FileOutputStream; import java.io.IOException; public class ItextWatermark { public static void main(String[] args) throws IOException, DocumentException { // 读取原始 PDF 文件 PdfReader reader = new PdfReader("original.pdf"); PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("output.pdf")); // 获取 PDF 中的页数 int pageCount = reader.getNumberOfPages(); // 添加水印 for (int i = 1; i <= pageCount; i++) { PdfContentByte contentByte = stamper.getUnderContent(i); // 或者 getOverContent() contentByte.beginText(); contentByte.setFontAndSize(BaseFont.createFont(), 36f); contentByte.setColorFill(BaseColor.LIGHT_GRAY); contentByte.showTextAligned(Element.ALIGN_CENTER, "Watermark", 300, 400, 45); contentByte.endText(); } // 保存修改后的 PDF 文件并关闭文件流 stamper.close(); reader.close(); } }方式三:用 Ghostscript 命令行Ghostscript 是一款流行的、免费的、开源的 PDF 处理程序,它可以用来创建、读取、修改和提取 PDF 内容。 Ghostscript 中提供了命令行参数来添加水印。{mtitle title="Ghostscrip"/}首先需要在本地安装 Ghostscript 程序。可通过以下链接下载安装包:{mtitle title="添加水印"/}可以在终端中使用 Ghostscript 的命令行工具执行以下命令来实现:gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=output.pdf -c "newpath /Helvetica-Bold findfont 36 scalefont setfont 0.5 setgray 200 200 moveto (Watermark) show showpage" original.pdf上述命令中,-sDEVICE=pdfwrite 表示输出为 PDF 文件;-sOutputFile=output.pdf 表示输出文件名为 output.pdf ;最后一个参数 original.pdf 则表示原始 PDF 文件的路径;中间的字符串则表示添加的水印内容。{mtitle title="注意事项"/}使用 Ghostscript 命令行添加水印时,会直接修改原始 PDF 文件,因此建议先备份原始文件。方式四:Free Spire.PDF for Java下面介绍一下使用 Free Spire.PDF for Java 实现 PDF 添加水印的方式。Free Spire.PDF for Java 是一款免费的 Java PDF 库,它提供了一个简单易用的 API,用于创建、读取、修改和提取 PDF 内容。Free Spire.PDF for Java 也支持添加 文本水印 以及 图片水印。{mtitle title="添加 Free Spire.PDF for Java 依赖"/}首先,在 pom.xml 文件中添加 Free Spire.PDF for Java 的依赖:<dependency> <groupId>e-iceblue</groupId> <artifactId>free-spire-pdf-for-java</artifactId> <version>1.9.6</version> </dependency>{mtitle title="添加文本水印"/}在 添加水印 之前,需要读取原始 PDF 文件:PdfDocument pdf = new PdfDocument(); pdf.loadFromFile("original.pdf");然后,遍历 PDF 中的所有页面,并使用 PdfPageBase 添加水印:// 遍历 PDF 中的所有页面 for (int i = 0; i < pdf.getPages().getCount(); i++) { PdfPageBase page = pdf.getPages().get(i); // 添加文本水印 PdfWatermark watermark = new PdfWatermark("Watermark"); watermark.setFont(new PdfFont(PdfFontFamily.Helvetica, 36)); watermark.setOpacity(0.5f); page.getWatermarks().add(watermark); }最后,需要保存修改后的 PDF 文件:pdf.saveToFile("output.pdf"); pdf.close();{mtitle title="添加图片水印"/}添加 图片水印 与 添加文本水印 类似,只需要将 PdfWatermark 的参数修改为图片路径即可。// 添加图片水印 PdfWatermark watermark = new PdfWatermark("watermark.png"); watermark.setOpacity(0.5f); page.getWatermarks().add(watermark);{mtitle title="完整代码"/}下面是使用 Free Spire.PDF for Java 来实现 PDF 添加水印的完整代码:import com.spire.pdf.*; public class FreeSpirePdfWatermark { public static void main(String[] args) { // 读取原始 PDF 文件 PdfDocument pdf = new PdfDocument(); pdf.loadFromFile("original.pdf"); // 遍历 PDF 中的所有页面 for (int i = 0; i < pdf.getPages().getCount(); i++) { PdfPageBase page = pdf.getPages().get(i); // 添加文本水印 PdfWatermark watermark = new PdfWatermark("Watermark"); watermark.setFont(new PdfFont(PdfFontFamily.Helvetica, 36)); watermark.setOpacity(0.5f); page.getWatermarks().add(watermark); // 添加图片水印 // PdfWatermark watermark = new PdfWatermark("watermark.png"); // watermark.setOpacity(0.5f); // page.getWatermarks().add(watermark); } // 保存修改后的 PDF 文件 pdf.saveToFile("output.pdf"); pdf.close(); } }方式五:Aspose.PDF for JavaAspose.PDF for Java 是一个强大的 PDF 处理库,提供了添加水印的功能。结合 Spring Boot 使用 Aspose.PDF for Java 库添加 PDF 水印的方式如下:首先,在 pom.xml 文件中添加 Aspose.PDF for Java 的依赖:<dependency> <groupId>com.aspose</groupId> <artifactId>aspose-pdf</artifactId> <version>21.4</version> </dependency>在 Spring Boot 应用程序中调用 Aspose.PDF for Java 的 API 设置 PDF 水印。{mtitle title="添加文本水印"/}@PostMapping("/addTextWatermark") public ResponseEntity<byte[]> addTextWatermark(@RequestParam("file") MultipartFile file) throws IOException { // 加载 PDF 文件 Document pdfDocument = new Document(file.getInputStream()); TextStamp textStamp = new TextStamp("Watermark"); textStamp.setWordWrap(true); textStamp.setVerticalAlignment(VerticalAlignment.Center); textStamp.setHorizontalAlignment(HorizontalAlignment.Center); pdfDocument.getPages().get_Item(1).addStamp(textStamp); // 保存 PDF 文件 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); pdfDocument.save(outputStream); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"watermarked.pdf\"") .contentType(MediaType.APPLICATION_PDF) .body(outputStream.toByteArray()); }{mtitle title="添加图片水印"/}@PostMapping("/addImageWatermark") public ResponseEntity<byte[]> addImageWatermark(@RequestParam("file") MultipartFile file) throws IOException { // 加载 PDF 文件 Document pdfDocument = new Document(file.getInputStream()); ImageStamp imageStamp = new ImageStamp("watermark.png"); imageStamp.setWidth(100); imageStamp.setHeight(100); imageStamp.setVerticalAlignment(VerticalAlignment.Center); imageStamp.setHorizontalAlignment(HorizontalAlignment.Center); pdfDocument.getPages().get_Item(1).addStamp(imageStamp); // 保存 PDF 文件 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); pdfDocument.save(outputStream); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"watermarked.pdf\"") .contentType(MediaType.APPLICATION_PDF) .body(outputStream.toByteArray()); }注意,以上代码中的文件名、宽度、高度等参数需要根据实际情况进行调整。{mtitle title="完整代码"/}完整的 Spring Boot 控制器类代码如下:import com.aspose.pdf.*; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.ByteArrayOutputStream; import java.io.IOException; @RestController @RequestMapping("/api/pdf") public class PdfController { @PostMapping("/addTextWatermark") public ResponseEntity<byte[]> addTextWatermark(@RequestParam("file") MultipartFile file) throws IOException { // 加载 PDF 文件 Document pdfDocument = new Document(file.getInputStream()); TextStamp textStamp = new TextStamp("Watermark"); textStamp.setWordWrap(true); textStamp.setVerticalAlignment(VerticalAlignment.Center); textStamp.setHorizontalAlignment(HorizontalAlignment.Center); pdfDocument.getPages().get_Item(1).addStamp(textStamp); // 保存 PDF 文件 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); pdfDocument.save(outputStream); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"watermarked.pdf\"") .contentType(MediaType.APPLICATION_PDF) .body(outputStream.toByteArray()); } @PostMapping("/addImageWatermark") public ResponseEntity<byte[]> addImageWatermark(@RequestParam("file") MultipartFile file) throws IOException { // 加载 PDF 文件 Document pdfDocument = new Document(file.getInputStream()); ImageStamp imageStamp = new ImageStamp("watermark.png"); imageStamp.setWidth(100); imageStamp.setHeight(100); imageStamp.setVerticalAlignment(VerticalAlignment.Center); imageStamp.setHorizontalAlignment(HorizontalAlignment.Center); pdfDocument.getPages().get_Item(1).addStamp(imageStamp); // 保存 PDF 文件 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); pdfDocument.save(outputStream); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"watermarked.pdf\"") .contentType(MediaType.APPLICATION_PDF) .body(outputStream.toByteArray()); } }这里使用了两个 RESTful API :/addTextWatermark 和 /addImageWatermark ,分别用于添加 文本水印 和 图片水印 。在请求中通过 file 参数传递 PDF 文件。下面介绍如何使用 Postman 来测试 Spring Boot 应用程序的 API。下载并安装 Postman 。打开 Postman ,选择 POST 请求方法。在 URL 地址栏中输入 http://localhost:8080/api/pdf/addTextWatermark 。在 Headers 标签页中设置 Content-Type 为 multipart/form-data 。在 Body 标签页中选择 form-data 类型,然后设置 key 为 file ,value 选择本地的 PDF 文件。点击 Send 按钮发送请求,等待应答结果。处理结果将会在响应的 Body 中返回,也可以选择浏览器下载或保存到本地磁盘。以上就是使用 Aspose.PDF for Java 库结合 Spring Boot 添加 PDF 水印的方式。结论本文介绍了几种使用 Spring Boot 实现 PDF 添加水印的方式,分别是使用 Apache PDFBox 库、 iText 库以及 Ghostscript 命令行等。选择哪种方式,可以根据项目需求和个人偏好来决定。无论采用哪种方式,都需要注意保护原始 PDF 文件,不要在不必要的情况下直接修改原始文件。欢迎点赞收藏,在你老板安排你干这时,希望你能够及时找到相关的Java工具库,实现这项功能。
2023年07月03日
39 阅读
0 评论
0 点赞
2023-07-03
一款开源的匿名聊天工具,太牛了
今天推荐的这个项目是 「anonymous-chat-room 」 ,一个基于 livekit 和 Next.js 的匿名聊天室,可以进行文字、语音聊天,并支持语音录屏。特点部署简单:前端支持直接部署到 vercel ,后端可以直接使用 livekit cloud 的免费服务,也可以按照官方文档自建支持视频,语音聊天(默认只允许语音),无需登录支持文本聊天,也可以使用 emoji支持emoji表情搜索支持更多的消息类型,如图片、视频(开发中)支持浏览器直接录制麦克风,扬声器和屏幕(Chrome、 Edge 可以完全支持,safari 不支持扬声器录制)延迟测试(当前版本实现很简陋)设置房间密码前端可以选择使用多个 apikey ,通过轮询的方式选择可用的入口本地部署1、克隆或下载本仓库git clone git@github.com:velor2012/anonymous-chat-room.git cd anonymous-chat-room yarn install2、在 http://cloud.livekit.io 上创建一个新的 Project ,然后生成 apikey project settings 3、按照提示,修改 env.example 中的环境变量,重命名为 env.local 4、运行以下命令npm run dev之后就可以在 http://localhost:3000 打开。在线体验地址: https://chat.cwy666.eu.org/开源项目地址: https://github.com/velor2012/anonymous-chat-room
2023年07月03日
103 阅读
0 评论
0 点赞
2023-06-29
群晖 DMS7.0 Video Station 支持 DTS 和 eac3 解决方案
Video Station 一直是我必装并且使用频繁的套件。但一直有个问题比较困扰,如果下载的是高清视频,经常会提示不支持当前所选音频的文件格式,因此无法播放视频。请尝试其它音轨。 具体原因我不过多赘述,在网上找了一些方案基本都是搬运,所以我翻阅了很多资料,经过尝试之后成功解决。在这里把过程记录下来,各位如果有相同问题的可以用来参考。下面开始操作。首先安装社区版FFMPEG社区地址:http://packages.synocommunity.com套件中心 > 社群 > ffmpeg开启SSH可设置自定义端口或使用默认端口使用终端连接群晖依次输入以下命令:# 切换到root权限,回车后输入管理员密码即可 sudo -i # 保存 Video Station 使用的 ffmpeg mv -n /var/packages/VideoStation/target/bin/ffmpeg /var/packages/VideoStation/target/bin/ffmpeg.orig # 注入脚本 wget -O - https://gist.githubusercontent.com/BenjaminPoncet/bbef9edc1d0800528813e75c1669e57e/raw/ffmpeg-wrapper > /var/packages/VideoStation/target/bin/ffmpeg # 更改脚本的所有权和模式 chown root:VideoStation /var/packages/VideoStation/target/bin/ffmpeg chmod 750 /var/packages/VideoStation/target/bin/ffmpeg chmod u+s /var/packages/VideoStation/target/bin/ffmpeg # 保存 Video Station 的 libsynovte.so cp -n /var/packages/VideoStation/target/lib/libsynovte.so /var/packages/VideoStation/target/lib/libsynovte.so.orig chown VideoStation:VideoStation /var/packages/VideoStation/target/lib/libsynovte.so.orig # 使libsynovte.so 支持 DTS, EAC3, TrueHD sed -i -e 's/eac3/3cae/' -e 's/dts/std/' -e 's/truehd/dheurt/' /var/packages/VideoStation/target/lib/libsynovte.so替换群晖使用的 FFmpeg命令 调用的相关文件(注意DMS6.0和DMS7.0不同):DMS6.0mv /var/packages/CodecPack/target/bin/ffmpeg33 /var/packages/CodecPack/target/bin/ffmpeg33.orig cp /var/packages/VideoStation/target/bin/ffmpeg /var/packages/CodecPack/target/bin/ffmpeg33DMS7.0mv /var/packages/CodecPack/target/bin/ffmpeg41 /var/packages/CodecPack/target/bin/ffmpeg41.orig cp /var/packages/VideoStation/target/bin/ffmpeg /var/packages/CodecPack/target/bin/ffmpeg41重启Video Station套件通过套件中心进行Video Station的重启后续的更新更新只需要执行以下命令即可:wget -O - https://gist.githubusercontent.com/BenjaminPoncet/bbef9edc1d0800528813e75c1669e57e/raw/ffmpeg-wrapper > /var/packages/VideoStation/target/bin/ffmpeg还原与卸载如果出现了问题,或者不想用了。执行以下命令即可还原:# 还原 Video Station 官方使用的 ffmpeg 与 libsynovte.so mv -f /var/packages/VideoStation/target/bin/ffmpeg.orig /var/packages/VideoStation/target/bin/ffmpeg mv -f /var/packages/VideoStation/target/lib/libsynovte.so.orig /var/packages/VideoStation/target/lib/libsynovte.so mv -f /var/packages/CodecPack/target/bin/ffmpeg41.orig /var/packages/CodecPack/target/bin/ffmpeg41当然,你也可以在套件中心卸载重装 Video Station 套件,来达到还原的效果。操作完之后,一切正常,Synology Photos也都正常有缩略图和播放。又能愉快的通过Video Station套件看片了。
2023年06月29日
53 阅读
0 评论
0 点赞
2023-06-29
520表白html页 实现3D动态相册
前言:明天就是5月20号了赶紧做好给你女朋友给你喜欢的人表白3D动态相册,换成女朋友照片,顺畅丝滑,单身狗的吃狗粮吧,哈哈哈赶紧行动起来吧。预览:源码:下载上传即可,可二级目录。里面的照片在 images 文件夹修改就行,记得改名。另外,手机端看不到效果哦。{cloud title="520 3D相册源码" type="lz" url="https://wwua.lanzoup.com/isK0110q7prc" password=""/}
2023年06月29日
61 阅读
0 评论
0 点赞
1
...
12
13
14
...
27