在我們公司有一個運營部門,平常要負責把內容發布到抖音、快手、小紅書等這些平台。帳號類型也很多,有品牌號、合作號、還有專門用來做活動的測試號。日常的工作量其實不小。尤其一到促銷期或者活動期,一天要發好幾條筆記,而且要同時涵蓋不同的帳號。
每次到了活動期,運營小姐姐的電腦上都是好幾台手機和幾十張圖 + N 份文案堆在一起,一天下來要發好幾條筆記,還得確保不同帳號內容不重樣、不出錯。她經常一邊切帳號一邊自言自語:「這張圖是給哪個號的來著?我剛才發的是品牌號還是活動號?」
以前我們運營部門的流程是這樣的:
策劃把圖文和文案打包發給運營,運營打開文件,一條條複製粘貼、上傳圖片、切換帳號、點擊發布……
多帳號的時候還得來回切換登錄,有時候內容順序搞錯了,還得手動撤回重發,前功盡棄。
這種方式不僅效率低下、容易出錯,而且讓人精神高度緊張,運營小姐姐經常下班後還得回群裡道歉:「剛才發錯平台了,我重新發一遍……」
我每天下班的時候都能看到運營小姐姐還坐在工位上加班,發內容、切帳號、貼文案、盯發布進度,一天反反覆覆幾十次。到了活動高峰期,還得深夜返工,改圖換文案,一不小心還會貼錯內容、發錯帳號,前功盡棄。
面對運營小姐姐每天這樣重複性極高的內容工作流程,我還是看不下去了:
我決定要用技術打破這一切機械操作。
於是我打算做一套 「內容統一管理 + 多平台分享集成」 的系統。
所有圖文內容、發布素材和話題信息,運營可以提前在後台配置好。確認無誤後,只需點擊「一鍵生成分享鏈接」,把鏈接分享到對應設備上,打開就能自動跳轉各平台的發布界面,文案和圖片都自動填充,徹底告別人工複製粘貼。
從「人工發圖」變成「鏈接跳轉」,
從「複製粘貼」變成「一鍵分發」,
用一行代碼,換運營十分鐘。
運營小姐姐只需要點一下,就能完成過去十幾分鐘的手工操作。把效率提上來,錯誤率降下去,讓小姐姐能準點下班。
那本篇文章我們就先單獨講一下如何實現用一條鏈接一鍵直接將圖文和視頻內容分享到小紅書 App 筆記裡的功能。
我們先來看一下平常在小紅書 App 手動發布筆記的流程,通常會先點擊首頁下方的加號按鈕,選擇要上傳的圖片或視頻,然後輸入標題和正文,再選擇相關話題,確認無誤後點擊發布按鈕完成發布。下面我放一張示意圖方便大家直觀理解整個流程。
如果是多帳號運營,這個過程就要重複好幾次,效率非常低。等我們設計好架構接入技術之後,我們就能把整個流程程序化,把要發布的標題、內容、圖片集中整理到一個統一的後台頁面。運營先添加預發布的文案,在後台點擊預覽確認沒有問題後,就可以生成一個分享鏈接,再把鏈接發給其他負責發布的同學。然後打開鏈接就能直接預覽,並且一鍵快速發布,這就是我們希望實現的最終效果。
由於項目真實後台涉及敏感配置和接口調用,這裡我們用一個簡單後台來說明功能設計流程。以下是一個我自己簡單搭建的小紅書文案管理後台,主要分為兩個頁面:文案列表頁和發布頁,分別用於管理文案和錄入新文案。
我們先來看文案列表頁,如下圖展示:
它的主要功能是展示所有待發布或已發布的文案記錄。每條文案包括封面圖、標題、正文內容、圖文數量,以及一組操作按鈕,支持「複製分享鏈接」、「預覽」、「編輯」和「刪除」。通過這些操作,我們可以快速查閱、預覽和維護已有文案。
右上角是搜索框和「新建文案」按鈕,運營可以通過關鍵詞快速查找歷史文案,也可以點擊新建按鈕跳轉到發布頁。
下面是發布頁的示意圖,如圖所示:
在這個頁面中,我們可以填寫文案的標題和正文內容,正文支持 Markdown 格式,方便排版。接著我們可以從素材庫中選擇需要發布的圖片或視頻內容。填寫完成後,點擊底部的「發布文案」按鈕,即可提交到後台數據庫中。
整個後台的核心流程是:運營先在發布頁準備好圖文內容並保存,之後可以回到列表頁複製分享鏈接,將鏈接發送給需要發布的同學,對方點擊鏈接即可跳轉到預覽頁,一鍵發布到小紅書 App 筆記。這種方式大大簡化了手動發布流程,尤其適合多帳號分工運營場景,能極大提升效率,減少重複勞動。
後台文案管理功能設計
為了更方便地管理和發布圖文內容,我們設計了一個簡單的小紅書後台系統。後台主要由兩個核心頁面組成:文案列表頁 和 發布頁,分別用於查看、維護和錄入要發布的圖文素材。
後台的數據存儲基於一張 x_post
表,結構設計如下:
CREATE TABLE `x_post` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`type` varchar(20) NOT NULL COMMENT '筆記類型 normal | video',
`title` varchar(255) DEFAULT NULL COMMENT '標題',
`content` text COMMENT '正文內容',
`cover_url` varchar(500) DEFAULT NULL COMMENT '封面圖',
`image_urls` text COMMENT '圖片列表,JSON數組格式',
`video_url` varchar(500) DEFAULT NULL COMMENT '視頻地址',
`is_deleted` tinyint(1) DEFAULT 0 COMMENT '是否刪除 0=未刪除 1=已刪除',
`deleted_at` datetime DEFAULT NULL COMMENT '刪除時間',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='發布筆記表';
這張表記錄了每篇小紅書筆記的關鍵信息,包括標題、內容、封面、圖文或視頻鏈接、刪除時間和是否刪除等字段。其中 image_urls
是一個 JSON 數組格式,便於存儲多圖信息,type
字段區分圖文和視頻類型。
內容創建完後,運營小姐姐就可以通過後台的發布頁錄入一篇新文案,然後在列表頁點擊「預覽」按鈕來查看內容展示效果。(其實我們還可以支持一鍵導入文案素材的功能)
例如下圖就是我們第一篇文案的預覽效果頁面:
這個預覽頁面完全模擬小紅書 App 內的發布效果,頂部是封面圖和內容圖集,下面是正文,包括 emoji、換行、圖文排版、交通路線推薦等內容。
如果是多帳號運營,傳統做法需要登錄多個帳號重複上傳素材和手動複製粘貼內容,非常低效。而接入我們這個系統後,運營只需提前在後台準備好文案並生成預覽鏈接,分發給不同的發布同事即可。對方打開鏈接後可以直接查看效果,並一鍵完成發布操作。
這大大提升了運營效率,尤其是在有多個帳號需要同步發布或定時推送內容的場景中,非常實用。
這就是我們後台的整體設計思路。接下來,我們將詳細介紹如何與小紅書 App 進行聯動,实现一鍵直達發布頁的功能。通过技術手段進一步簡化發布流程,实现圖文內容從準備到落地的一體化閉環。
為了實現我們前面提到的「一鍵跳轉到小紅書發布頁」的能力,我們需要借助小紅書官方提供的 分享開放平台。它主要面向內容型或工具型的合作應用,提供了 SDK 和相關接口,支持 Android、iOS、HarmonyOS 和 JS 等多種平台。
從官方說明來看,當前支持的能力包括:
這些功能意味著,我們不需要自行設計複雜的內容編輯器,也不需要從零搭建視頻發布模塊,只需準備好基礎內容,通過官方提供的 SDK 喚起小紅書 App,即可完成整個跳轉發布流程。
下面是我們使用小紅書JS SDK整個接入流程的概覽圖:
從圖中可以看出,整個分享流程包括如下關鍵步驟:
整個 JS SDK 鑑權流程主要包括以下三步:
第一步:獲取 access_token
第一次簽名操作,使用 appKey、appSecret 等生成加簽參數,向 openAPI 請求 access_token
。
第二步:生成 JS 簽名 signature
獲取到 access_token 後,使用 appKey + nonce + timestamp + access_token
進行再次簽名,生成 JS SDK 所需的 signature
。
第三步:返回簽名參數給前端
服務端將 appKey
、nonce
、timestamp
和 signature
返回給第三方前端,前端可使用 JS SDK 調用分享方法,並喚起小紅書 App。
在完成了前面的接入流程和鑑權簽名後,我們最終要調用的是小紅書 JS SDK 提供的分享方法 xhs.share()
,這個方法可以讓我們喚起小紅書 App,並快速填充好要發布的內容。
調用格式大致如下:
xhs.share({
shareInfo: {
type: 'normal', // 筆記類型,圖文用 'normal',視頻用 'video'
title: '...', // 筆記標題(可選)
content: '...', // 筆記正文內容(可選)
images: ['...'], // 圖文筆記必填,必須是公網圖片地址
video: '...', // 視頻筆記必填,必須是公網視頻地址
cover: '...' // 視頻封面圖,必須是公網地址
},
verifyConfig: {
appKey: '...', // 平台分配的應用唯一標識
nonce: '...', // 服務端生成的隨機字符串
timestamp: '...', // 服務端生成的時間戳
signature: '...' // 服務端生成的簽名
},
fail: (e) => {
// 調用失敗時的處理邏輯
}
})
這裡的 shareInfo
是我們希望填入小紅書發布頁面的內容信息,主要包括筆記類型(圖文 or 視頻)、標題、正文、圖片或視頻地址等;而 verifyConfig
是服務端提前生成的簽名鑑權參數,必須由服務器返回,不能由前端自己生成。
下面是每個字段的含義說明:
字段名 | 是否必填 | 類型 | 說明 |
---|---|---|---|
type | 是 | String | 筆記類型,支持 normal (圖文)和 video (視頻) |
title | 否 | String | 筆記標題 |
content | 否 | String | 筆記文字內容 |
images | 否 | Array | 圖文類型必填,圖片列表,必須是公網地址,不支持本地路徑 |
video | 否 | String | 視頻類型必填,視頻文件地址,必須是公網地址 |
cover | 否 | String | 視頻封面圖,必須是公網地址 |
appKey | 是 | String | 應用在小紅書開放平台申請到的唯一標識 |
nonce | 是 | String | 服務器生成的隨機字符串,用於簽名 |
timestamp | 是 | String | 服務器生成的時間戳,用於簽名 |
signature | 是 | String | 服務器生成的簽名結果 |
fail | 否 | Function | 分享失敗時的回調方法,可用於提示或埋點 |
需要注意的是,小紅書的 xhs.share()
方法內置了鑑權流程,我們只需要提供服務端生成的 nonce
、timestamp
和 signature
,前端不需要自己參與簽名過程。
上面我們已經介紹了接入小紅書分享平台 JS SDK 的整個流程,包括平台能力、簽名機制以及前後端的配合方式。
接下來我將通過一個簡化版本的服務端接口與前端 HTML 頁面,來實現一個最小閉環的一鍵發布功能 Demo,幫助大家快速理解整個流程的落地實現。
在服務端的邏輯裡,我們通常只需要實現兩個接口:一個是文案預覽接口,另一個是一鍵發布時用到的分享參數生成接口。
文案預覽接口的作用是:當我們從後台生成了一個分享鏈接發給同事後,對方打開這個鏈接時,前端需要能展示出這條小紅書文案的內容,比如標題、正文、圖片等信息。這時候頁面會攜帶一個唯一的文案 ID(通常是鏈接裡的參數),服務端需要根據這個 ID 去數據庫查一下這條文案是否存在、有哪些被下架等狀態校驗,然後把文案信息返回給前端做展示。
而一鍵發布接口的作用是:當用戶點擊「立即發布」按鈕後,前端需要去服務端調用一個接口,這個接口的任務是再次校驗文案是否有效,並根據我們接入小紅書分享平台的要求,生成所需的簽名參數(比如 appKey、nonce、timestamp、signature 等),這些參數會被前端 JS-SDK 拿去喚起小紅書 App,實現真正的跳轉和發布功能。
在我們的後台中,每一條小紅書文案都支持「點擊生成分享鏈接」的功能。這個分享鏈接通常會附帶一個唯一標識 ID,例如:
https://xxxx.com/share.html?_id=ABY1boZllzPlTiq2
這個 _id
就是我們為每一條文案生成的唯一標識,目的是為了讓運營或其他同事拿到鏈接後可以打開預覽頁面,並看到這條筆記的內容細節。
我們服務端的預覽接口設計也非常簡單,主要邏輯如下:
控制器代碼
/**
* 預覽接口
* 示例:GET /api/post/preview/1
*/
@GetMapping("/preview/{id}")
public ResponseEntity<XPost> preview(@PathVariable Long id) {
return ResponseEntity.ok(postService.previewPost(id));
}
為了方便演示,這裡我們使用的是通過 GET
請求訪問接口,並直接返回數據庫中的文案信息。實際項目中可以根據自己的需求使用更安全或更複雜的方式來處理分享鏈接的鑑權與數據訪問。
Service 層邏輯
public XPost previewPost(Long id) {
return postRepository.findById(id)
.orElseThrow(() -> new RuntimeException("內容不存在"));
}
這個方法會根據文案 ID 去數據庫中查詢對應的數據。如果找不到,就直接拋出異常提示「內容不存在」。
通過這個接口,我們就可以在分享鏈接打開時,自動去服務端拉取對應文案的數據,並渲染在頁面中,方便運營預覽確認。
前端預覽頁面展示效果
在前面我們已經通過服務端接口拿到了某條文案的詳細內容(如圖片列表、正文文案等)。這一步我們會基於這些數據,用一個簡單的 HTML 頁面來完成「預覽頁面」的渲染展示。
這個頁面的作用是:當我們在後台生成分享鏈接並複製給運營後,運營或用戶可以通過這個鏈接在瀏覽器中打開文案預覽頁面,確認發布內容是否正確。如果確認無誤,還可以點擊「一鍵發布」按鈕,跳轉至小紅書 App 完成最終發布。
示例展示效果
如下圖所示,這是我們通過微信打開渲染出來的預覽頁面效果:
我這個demo頁面使用了原生 HTML + Tailwind CSS + Swiper 輪播圖組件來實現這個頁面。代碼邏輯非常簡單:
/api/post/preview/{id}
獲取文案內容;imageUrls
渲染輪播圖;content
渲染正文文案;下面是部分部分代碼片段:
<!-- 圖片輪播區域 -->
<div class="swiper">
<div class="swiper-wrapper" id="preview-images"></div>
<div class="swiper-pagination"></div>
</div>
<!-- 文案內容 -->
<div class="xhs-text" id="preview-text">加載中...</div>
<!-- 一鍵發布按鈕 -->
<button class="btn-publish" onclick="publishPost()">發布到小紅書</button>
我們通過簡單的輪播圖 + 段落渲染的方式,讓用戶在手機上也可以便捷查看預覽效果。整個頁面在移動端有良好的顯示效果,支持微信、瀏覽器等環境打開。
在完成了預覽頁面後,接下來我們要實現的就是 一鍵發布接口。這個接口的核心目標是:
當用戶在預覽頁面點擊「發布到小紅書」按鈕後,後端生成一組必要的簽名參數,並返回給前端,前端再用這組參數跳轉到小紅書 App 完成筆記發布。
接口定義
我們定義了如下接口來支持這個過程:
/**
* 發布接口
* 示例:GET /api/post/publish/1
*/
@GetMapping("/publish/{id}")
public ResponseEntity<Map<String, Object>> publish(@PathVariable Long id) {
Map<String, Object> result = postService.buildPublishResult(id);
return ResponseEntity.ok(result);
}
這個接口中,id
表示文案在數據庫中的主鍵 ID。我們通過這個 ID 查詢文案內容,並為其生成一組帶簽名的跳轉參數。
配置小紅書開放平台的 AppKey 和 AppSecret
在 Spring Boot 的 application.yml
配置文件中,我們提前配置好了小紅書開放平台的 appKey
和 appSecret
,用於生成簽名參數:
# 小紅書分享開放平台配置
xhs:
app-key: xxxxxxx
app-secret: xxxxxxx
核心方法:構建發布參數
接口底層調用的邏輯是:
public Map<String, Object> buildPublishResult(Long id) {
XPost post = previewPost(id); // 獲取筆記內容
// 生成簽名部分
Map<String, Object> data = generateSignature(id);
// 構建分享內容
Map<String, Object> shareInfo = new HashMap<>();
shareInfo.put("type", post.getType());
shareInfo.put("title", post.getTitle());
shareInfo.put("content", post.getContent());
shareInfo.put("images", ImageUtils.parseImages(post.getImageUrls()));
shareInfo.put("cover", Optional.ofNullable(post.getCoverUrl())
.orElseGet(() -> ImageUtils.getFirstImage(post.getImageUrls())));
shareInfo.put("time", System.currentTimeMillis());
shareInfo.put("_id", "xhs_" + post.getId());
Map<String, Object> result = new HashMap<>();
result.put("data", data); // 簽名字段
result.put("shareInfo", shareInfo); // 文案內容
return result;
}
/**
* 生成小紅書發布參數簽名
* @param postId 文案ID
* @return appKey + nonce + timestamp + signature
*/
public Map<String, Object> generateSignature(Long postId) {
// 查詢是否存在
XPost post = postRepository.findById(postId)
.orElseThrow(() -> new RuntimeException("內容不存在"));
String appKey = xhsProperties.getAppKey();
String appSecret = xhsProperties.getAppSecret();
String nonce = UUID.randomUUID().toString().replace("-", "").substring(0, 12);
String timestamp = String.valueOf(System.currentTimeMillis());
try {
String signature = SignatureUtil.buildSignature(appKey, nonce, timestamp, appSecret);
Map<String, Object> result = new HashMap<>();
result.put("appKey", appKey);
result.put("nonce", nonce);
result.put("timestamp", timestamp);
result.put("signature", signature);
return result;
} catch (Exception e) {
throw new RuntimeException("生成簽名失敗: " + e.getMessage(), e);
}
}
接口返回的結果分為兩部分:
data
部分:用於跳轉小紅書所需的簽名參數,包括 appKey
、nonce
(隨機字符串)、timestamp
(時間戳)、signature
(簽名值),這些字段將作為 xhs.share()
方法中的 verifyConfig
參數傳入;shareInfo
部分:是我們準備要發布的筆記內容,包括 type
(圖文 or 視頻)、title
(標題)、content
(正文)、images
(圖片地址列表)、cover
(封面圖)等信息,用於在前端渲染展示和傳遞給小紅書 SDK。我們測試時實際返回的結構如下所示:
{
"shareInfo": {
"cover": "http://110.42.233.124/images/dishini.jpg",
"images": [
"http://110.42.233.124/images/dishini.jpg",
"http://110.42.233.124/images/dishini2.jpg",
"http://110.42.233.124/images/dishini3.jpg"
],
"time": 1758188642421,
"_id": "xhs_1",
"type": "normal",
"title": "一張圖講清楚上次迪士尼交通攻略🚇",
"content": "一張圖講清楚上次迪士尼交通攻略🚇\n有多少寶子去迪士尼光是研究交通就費了好大勁。。。"
},
"data": {
"signature": "2486268ecfa3886b7fc4cd1d1fcbda5381b3177f96daf5f1e4dd09b938f94d4f",
"appKey": "red.96juc55xxxx",
"nonce": "10e07cfc400c",
"timestamp": 1758188642420
}
}
前端拿到這組數據後,只需要將 shareInfo
和 data
傳入小紅書 SDK 的 xhs.share()
方法中,即可完成跳轉與筆記預填。
簽名生成工具類:SignatureUtil.java
小紅書分享 SDK 的調用需要進行簽名認證,為此我們封裝了一個簡單的工具類,用於生成 SHA256 簽名,核心思路是:
appKey
、nonce
、timestamp
等參數排序;key=value&key2=value2...
的格式後追加 appSecret
;/**
* 小紅書簽名生成工具類
*/
public class SignatureUtil {
/**
* 構建簽名
*
* @param appKey 應用唯一標識
* @param nonce 隨機字符串(建議12位)
* @param timestamp 當前時間戳(毫秒)字符串格式
* @param appSecret 應用密鑰
* @return 簽名字符串(SHA-256 Hex)
*/
public static String buildSignature(String appKey, String nonce, String timestamp, String appSecret) throws Exception {
Map<String, String> params = new HashMap<>();
params.put("appKey", appKey);
params.put("nonce", nonce);
params.put("timestamp", timestamp);
return generateSignature(appSecret, params);
}
/**
* 簽名生成邏輯
* @param secretKey 秘鑰
* @param params 參數 map
* @return 簽名字符串
*/
public static String generateSignature(String secretKey, Map<String, String> params) {
// Step 1: 排序參數
Map<String, String> sortedParams = new TreeMap<>(params);
// Step 2: 拼接參數字符串(key=value&...)+ secretKey
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
if (sb.length() > 0) sb.append("&");
sb.append(entry.getKey()).append("=").append(entry.getValue());
}
sb.append(secretKey); // 拼接密鑰
// Step 3: 進行 SHA256 簽名
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = md.digest(sb.toString().getBytes(StandardCharsets.UTF_8));
// 轉十六進制
StringBuilder hex = new StringBuilder();
for (byte b : hashBytes) {
hex.append(String.format("%02x", b));
}
return hex.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA-256算法不可用", e);
}
}
}
圖片處理工具類:ImageUtils.java
因為我們數據庫中 image_urls
字段是 JSON 陣列格式(如 ["url1", "url2", "url3"]
),前端展示和分享時需要將其解析成 List<String>
,我們封裝了如下工具類:
parseImages()
:將數據庫的 JSON 格式或逗號分隔的圖片字符串,解析為圖片地址列表。getFirstImage()
:獲取列表中的第一張圖,作為默認封面圖。/**
* 圖片處理工具類
*/
public class ImageUtils {
private static final ObjectMapper mapper = new ObjectMapper();
/**
* 將 imageUrls 字段解析為圖片地址列表
* @param imageUrls 數據庫中的 imageUrls 字段
* @return List<String> 圖片地址列表
*/
public static List<String> parseImages(String imageUrls) {
if (imageUrls == null || imageUrls.isBlank()) return Collections.emptyList();
try {
// 如果是 JSON 陣列格式
if (imageUrls.trim().startsWith("[")) {
return mapper.readValue(imageUrls, new TypeReference<List<String>>() {});
} else {
// 普通逗號分隔格式
return Arrays.stream(imageUrls.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
}
} catch (Exception e) {
return Collections.emptyList(); // 解析失敗返回空列表
}
}
/**
* 獲取圖片中的第一張圖(封面用)
* @param imageUrls 數據庫中的 imageUrls 字段
* @return 第一張圖片地址或 null
*/
public static String getFirstImage(String imageUrls) {
List<String> images = parseImages(imageUrls);
return images.isEmpty() ? null : images.get(0);
}
}
前端跳轉頁實現(跳轉小紅書 App 分享)
在我們前面實現的文案預覽頁中,點擊「一鍵發布」按鈕後,會跳轉到一個中間頁,這個頁面的作用是:
xhs.share()
方法,引導用戶跳轉