手把手教你做微信小程序:短视频去水印功能全实现
微信小程序搜索“抖猫高清去水印” 即可使用
大家好!最近很多做小程序开发的朋友问我:“想做一个短视频去水印工具,从前端到后端该怎么落地?” 确实,市面上的第三方去水印接口要么收费贵,要么不稳定,不如自己搭一套来得踏实。
今天就带大家从 0 到 1 实现微信小程序短视频去水印功能,涵盖前端交互、后端 API、部署上线全流程,代码直接复制就能用,新手也能轻松上手!

一、先搞懂:去水印不是 “消水印”,是 “找地址”
很多人以为去水印是用算法把视频里的水印擦掉,其实这是误区!主流平台的去水印核心是 “解析无水印源地址”—— 平台会同时存储带水印和无水印的视频文件,我们只要找到无水印文件的真实链接,就能直接播放或下载。
整个流程特别简单,就 4 步:
- 用户在小程序粘贴带水印视频链接(比如抖音 “复制链接”)
- 小程序把链接传给后端 API
- 后端识别平台(抖音 / 快手 / 小红书),解析出无水印地址
- 小程序拿到地址,展示视频并提供下载
是不是比想象中简单?接下来我们分前端、后端逐步实现。
二、前端小程序:30 分钟搭好交互界面
前端核心是 “让用户能输入链接、看结果、下载视频”,用微信原生小程序开发最省心,不用额外装框架。
1. 页面布局:简洁够用就好
在 pages/index/index.wxml 写页面结构,主要分 3 块:输入区、结果区、提示区,代码注释很详细,新手也能看懂:
<!-- 顶部标题 -->
<view class="page-title">短视频去水印工具</view>
<!-- 链接输入区 -->
<view class="input-wrap">
<input
class="url-input"
placeholder="粘贴抖音/快手/小红书视频链接"
bindinput="handleUrlInput"
value="{{videoUrl}}"
placeholder-class="input-placeholder"
/>
<!-- 解析按钮:没输入或解析中时禁用 -->
<button
class="parse-btn"
bindtap="handleParse"
disabled="{{!videoUrl || isParsing}}"
loading="{{isParsing}}"
>
{{isParsing ? "解析中..." : "立即解析"}}
</button>
</view>
<!-- 解析结果区:默认隐藏 -->
<view class="result-wrap" hidden="{{!isSuccess}}">
<!-- 视频封面 -->
<image class="video-cover" src="{{videoInfo.coverUrl}}" mode="widthFix" />
<!-- 视频标题 -->
<view class="video-title">{{videoInfo.title}}</view>
<!-- 无水印视频播放 -->
<video
class="video-player"
src="{{videoInfo.noWatermarkUrl}}"
controls
autoplay="{{false}}"
poster="{{videoInfo.coverUrl}}"
></video>
<!-- 下载按钮 -->
<button
class="download-btn"
bindtap="handleDownload"
open-type="openSetting"
bindopensetting="handleSettingChange"
>
保存到相册
</button>
<!-- 有效期提示 -->
<view class="tip-text">⚠️ 视频地址24小时内有效,失效请重新解析</view>
</view>
<!-- 错误提示:比如链接无效、解析失败 -->
<view class="error-tip" hidden="{{!errorMsg}}">{{errorMsg}}</view>
2. 样式美化:适配所有手机
在 pages/index/index.wxss 写样式,重点做好适配,避免在小屏手机上变形:
/* 全局样式重置 */
page {
background-color: #f5f5f5;
padding: 20rpx;
box-sizing: border-box;
}
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
text-align: center;
margin-bottom: 30rpx;
}
.input-wrap {
display: flex;
flex-direction: column;
gap: 20rpx;
margin-bottom: 30rpx;
}
.url-input {
padding: 22rpx;
background-color: #fff;
border-radius: 12rpx;
font-size: 28rpx;
color: #333;
border: 1rpx solid #eee;
}
.input-placeholder {
color: #999;
}
.parse-btn {
background-color: #27ae60;
color: #fff;
font-size: 30rpx;
padding: 20rpx 0;
border-radius: 12rpx;
}
.result-wrap {
background-color: #fff;
padding: 20rpx;
border-radius: 12rpx;
display: flex;
flex-direction: column;
gap: 20rpx;
}
.video-cover {
width: 100%;
border-radius: 8rpx;
}
.video-title {
font-size: 28rpx;
color: #333;
line-height: 44rpx;
max-height: 88rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.video-player {
width: 100%;
height: 400rpx;
border-radius: 8rpx;
background-color: #fafafa;
}
.download-btn {
background-color: #3498db;
color: #fff;
font-size: 30rpx;
padding: 20rpx 0;
border-radius: 12rpx;
margin-top: 10rpx;
}
.tip-text {
font-size: 24rpx;
color: #666;
text-align: center;
margin-top: 10rpx;
}
.error-tip {
color: #e74c3c;
font-size: 26rpx;
text-align: center;
background-color: #fff;
padding: 20rpx;
border-radius: 12rpx;
}
3. 核心逻辑:处理输入、解析、下载
在 pages/index/index.js 写交互逻辑,重点解决 3 个问题:输入校验、调用后端 API、保存视频到相册。关键代码都加了注释,跟着写就行:
Page({
data: {
videoUrl: "", // 用户输入的视频链接
isParsing: false, // 解析中状态(避免重复点击)
isSuccess: false, // 解析成功状态
videoInfo: {}, // 解析结果:标题/无水印地址/封面
errorMsg: "" // 错误提示
},
// 1. 监听输入框变化,保存链接
handleUrlInput(e) {
this.setData({ videoUrl: e.detail.value.trim() });
// 输入时清空之前的错误提示
if (this.data.errorMsg) {
this.setData({ errorMsg: "" });
}
},
// 2. 点击解析按钮,调用后端API
async handleParse() {
const { videoUrl } = this.data;
// 先简单校验链接:必须包含http/https
if (!/^https?:\/\/.+/.test(videoUrl)) {
this.setData({ errorMsg: "请粘贴有效的视频链接(如https://v.douyin.com/xxx)" });
return;
}
// 初始化状态:显示加载中,隐藏结果
this.setData({ isParsing: true, isSuccess: false, errorMsg: "" });
try {
// 生成签名:防止后端接口被别人滥用(和后端约定好的规则)
const sign = await this.generateSign(videoUrl);
// 调用后端解析接口(替换成你的后端域名)
const res = await wx.request({
url: "https://你的后端域名/api/remove-watermark",
method: "POST",
data: { url: videoUrl, sign },
header: { "content-type": "application/json" }
});
// 处理后端返回结果
const { code, msg, data } = res.data;
if (code === 200) {
// 解析成功:展示结果
this.setData({
isSuccess: true,
videoInfo: data,
isParsing: false
});
} else {
// 解析失败:显示错误信息
this.setData({
errorMsg: msg || "解析失败,请检查链接是否有效",
isParsing: false
});
}
} catch (err) {
// 捕获网络错误(比如没网、后端挂了)
this.setData({
errorMsg: "网络异常,请检查网络后重试",
isParsing: false
});
}
},
// 3. 生成签名:和后端用一样的加密规则(防止接口被滥用)
generateSign(url) {
const secret = "你的密钥"; // 必须和后端.env里的SECRET_KEY一致
// 微信小程序自带crypto模块,不用额外安装
const crypto = require("crypto");
return crypto.createHmac("sha256", secret)
.update(JSON.stringify({ url }))
.digest("hex");
},
// 4. 点击下载,保存视频到相册
async handleDownload() {
const { noWatermarkUrl } = this.data.videoInfo;
// 先检查是否有保存相册的权限
const authRes = await wx.getSetting();
if (!authRes.authSetting["scope.writePhotosAlbum"]) {
// 没有权限:弹框提示用户授权
wx.showModal({
title: "需要授权",
content: "请允许保存视频到你的相册",
success: (res) => {
if (res.confirm) {
// 跳转到授权设置页
wx.openSetting({ withSubscriptions: true });
}
}
});
return;
}
// 有权限:开始下载视频
wx.showLoading({ title: "正在保存..." });
try {
// 第一步:下载视频到小程序临时目录
const downloadRes = await wx.downloadFile({
url: noWatermarkUrl,
filePath: `${wx.env.USER_DATA_PATH}/watermark_free.mp4` // 临时存储路径
});
// 第二步:保存到手机相册
await wx.saveVideoToPhotosAlbum({
filePath: downloadRes.filePath
});
wx.hideLoading();
wx.showToast({ title: "保存成功!已在相册查看" });
} catch (err) {
wx.hideLoading();
wx.showToast({ title: "保存失败,请重试", icon: "none" });
}
},
// 5. 监听授权设置变化(比如用户从设置页回来)
handleSettingChange(e) {
// 如果用户开启了保存相册权限,自动触发下载
if (e.detail.authSetting["scope.writePhotosAlbum"]) {
this.handleDownload();
}
}
});
4. 权限配置:必不可少的一步
在 app.json 里声明 “保存相册” 的权限,否则小程序会报错:
{
"pages": ["pages/index/index"],
"window": {
"navigationBarTitleText": "短视频去水印",
"navigationBarBackgroundColor": "#fff",
"navigationBarTextStyle": "black"
},
"permission": {
"scope.writePhotosAlbum": {
"desc": "用于保存无水印视频到你的手机相册"
}
},
"sitemapLocation": "sitemap.json"
}
三、后端 API:Node.js 快速搭服务
后端核心是 “解析不同平台的视频链接”,用 Node.js + Express 开发最快,代码量少,部署也简单。
1. 先搭项目骨架
首先创建项目文件夹,执行 npm init -y 初始化,然后安装需要的依赖:
# 安装核心依赖
npm install express axios cheerio cors dotenv express-rate-limit
# 开发时用的依赖(热更新)
npm install nodemon --save-dev
然后在 package.json 里加脚本,方便启动服务:
{
"name": "watermark-api",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js", // 线上启动
"dev": "nodemon server.js" // 本地开发(热更新)
},
"dependencies": {
"axios": "^1.6.2",
"cheerio": "^1.0.0-rc.12",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-rate-limit": "^7.1.4"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}
创建 .env 文件存储敏感配置(比如端口、密钥),不要把这些写在代码里:
PORT=3000 # 服务端口
SECRET_KEY=your_secret_key # 和前端一致的签名密钥
TIMEOUT=5000 # 请求超时时间(毫秒)
RATE_LIMIT=30 # 每IP15分钟最多30次请求(防滥用)
RATE_LIMIT_WINDOW=900000 # 15分钟=900000毫秒
2. 入口文件:启动服务
创建 server.js 作为入口,配置跨域、请求限制,定义核心路由:
require('dotenv').config(); // 加载.env里的配置
const express = require('express');
const cors = require('cors');
const rateLimit = require('express-rate-limit');
const parserController = require('./controllers/parserController');
const app = express();
const PORT = process.env.PORT || 3000;
// 1. 跨域配置:只允许微信小程序访问(安全)
app.use(cors({
origin: ['https://servicewechat.com'],
credentials: true
}));
// 2. 请求限制:防止别人恶意刷接口
const limiter = rateLimit({
windowMs: process.env.RATE_LIMIT_WINDOW,
max: process.env.RATE_LIMIT,
message: { code: 429, msg: '请求太频繁啦,15分钟后再试~' }
});
app.use('/api', limiter); // 只对/api开头的接口生效
// 3. 解析JSON请求体(前端传的是JSON格式)
app.use(express.json());
// 4. 核心路由:短视频去水印解析
app.post('/api/remove-watermark', parserController.parseVideo);
// 5. 启动服务
app.listen(PORT, () => {
console.log(`服务已启动,访问地址:http://localhost:${PORT}`);
});
3. 关键工具:签名验证、平台识别
先写 2 个工具函数,解决 “签名验证” 和 “识别视频平台” 的问题。
(1)签名验证:防止接口被滥用
创建 utils/signVerify.js,确保只有你的小程序能调用接口:
const crypto = require('crypto');
/**
* 验证前端传的签名是否合法
* @param {Object} data - 要验证的数据(比如{url: "视频链接"})
* @param {string} sign - 前端传的签名
* @returns {boolean} 签名是否合法
*/
function verifySign(data, sign) {
const secret = process.env.SECRET_KEY;
// 和前端一样的加密逻辑:Hmac-SHA256 + hex编码
const generatedSign = crypto.createHmac('sha256', secret)
.update(JSON.stringify(data))
.digest('hex');
return generatedSign === sign;
}
module.exports = verifySign;
更多推荐



所有评论(0)