How Images Are Uploaded Programmatically in xiaohongshu-mcp: A Complete Technical Guide
The xiaohongshu-mcp project uses the Rod headless browser library to simulate user interactions with the Xiaohongshu web interface, injecting files directly into DOM file inputs and monitoring upload completion through preview element detection.
Programmatically uploading images to Xiaohongshu presents unique challenges due to the platform's reliance on client-side JavaScript and anti-automation measures. The xpzouying/xiaohongshu-mcp repository solves this by leveraging browser automation rather than reverse-engineered APIs. This article examines the complete technical implementation found in the source code, detailing how the system validates, injects, and verifies image uploads through the web interface.
The Core Upload Architecture
Browser Automation with Rod
At the heart of the upload system lies the Rod library, a Go-based devtools driver that controls Chrome or Edge via the Chrome DevTools Protocol. Unlike simple HTTP clients, Rod operates a real browser instance, allowing xiaohongshu-mcp to interact with the Xiaohongshu web app's React-based upload widgets as a legitimate user would.
The browser configuration resides in configs/browser.go, where headless mode, proxy settings, and timeout values are defined. These settings directly impact upload reliability, particularly when handling large image files or slow network conditions.
File Validation and Preparation
Before any browser interaction occurs, the system performs strict path validation in xiaohongshu/publish.go. Each provided image path undergoes an os.Stat check. Files that do not exist are immediately skipped with a warning log entry, preventing the browser from encountering broken file references that could stall the upload queue.
Valid paths are collected into a slice and processed sequentially. This sequential approach ensures that the Xiaohongshu web interface receives files at a human-like pace, reducing the risk of rate limiting or upload corruption.
Step-by-Step Upload Implementation in publish.go
The uploadImages function in xiaohongshu/publish.go orchestrates the entire process. It combines DOM manipulation with polling-based verification to ensure each image reaches Xiaohongshu's servers before proceeding.
Path Validation and Filtering
The function begins by filtering the input slice:
validPaths := []string{}
for _, p := range imagesPaths {
if _, err := os.Stat(p); os.IsNotExist(err) {
logrus.Warnf("图片文件不存在: %s", p)
continue
}
validPaths = append(validPaths, p)
logrus.Infof("获取有效图片:%s", p)
}
This defensive programming pattern ensures that only accessible files are passed to the browser automation layer.
Sequential File Injection
Images upload one at a time. The function distinguishes between the first image and subsequent images using different CSS selectors:
- First image: Uses
.upload-input(the visible "Add Image" button) - Subsequent images: Uses
input[type="file"](the hidden file input that remains in the DOM)
for i, path := range validPaths {
selector := `input[type="file"]`
if i == 0 {
selector = ".upload-input"
}
uploadInput, err := page.Element(selector)
if err != nil {
return errors.Wrapf(err, "查找上传输入框失败(第%d张)", i+1)
}
if err := uploadInput.SetFiles([]string{path}); err != nil {
return errors.Wrapf(err, "上传第%d张图片失败", i+1)
}
slog.Info("图片已提交上传", "index", i+1, "path", path)
// ... wait for completion
}
The Element.SetFiles method is the critical Rod API call that injects the local file path into the browser's file input element, triggering the Xiaohongshu frontend's upload handler.
Upload Completion Detection
Since browser-based uploads occur asynchronously, the system polls the DOM for visual confirmation. The waitForUploadComplete function monitors the appearance of preview thumbnails:
func waitForUploadComplete(page *rod.Page, expectedCount int) error {
maxWaitTime := 60 * time.Second
checkInterval := 500 * time.Millisecond
start := time.Now()
lastLogCount := expectedCount - 1
for time.Since(start) < maxWaitTime {
uploadedImages, err := page.Elements(".img-preview-area .pr")
if err != nil {
time.Sleep(checkInterval)
continue
}
currentCount := len(uploadedImages)
if currentCount != lastLogCount {
slog.Info("等待图片上传", "current", currentCount, "expected", expectedCount)
lastLogCount = currentCount
}
if currentCount >= expectedCount {
slog.Info("图片上传完成", "count", currentCount)
return nil
}
time.Sleep(checkInterval)
}
return errors.Errorf("第%d张图片上传超时(60s),请检查网络连接和图片大小", expectedCount)
}
This polling mechanism checks for the .img-preview-area .pr selector every 500 milliseconds, with a hard timeout of 60 seconds. The function returns successfully only when the number of preview elements matches the expected upload count, ensuring that subsequent publishing steps only execute after all images are fully processed by Xiaohongshu's servers.
Integration with the Publish Workflow
The upload functionality integrates into the broader publishing pipeline through the PublishAction struct. When a user invokes the Publish method (defined in the service layer), the system executes the following sequence:
- Browser context initialization – Ensures a logged-in Rod page is available
- Image upload phase – Calls
uploadImages(page, content.ImagePaths)as detailed above - Content population – Fills title, body text, and tags using DOM selectors
- Final submission – Clicks the publish button after confirming all uploads completed
This architecture decouples the upload mechanism from content creation, allowing the same uploadImages function to be reused across different content types (standard posts, carousel posts, etc.).
Summary
- xiaohongshu-mcp leverages the Rod headless browser library to automate image uploads through the official Xiaohongshu web interface rather than using undocumented APIs.
- The upload logic resides primarily in
xiaohongshu/publish.go, specifically within theuploadImagesandwaitForUploadCompletefunctions. - Path validation occurs before browser interaction, filtering out non-existent files to prevent automation failures.
- Sequential upload uses different CSS selectors for the first image (
.upload-input) versus subsequent images (input[type="file"]), withElement.SetFiles()injecting file paths into the DOM. - Completion detection relies on polling for preview thumbnail elements (
.img-preview-area .pr) with a 60-second timeout, ensuring uploads finish before proceeding to publish.
Frequently Asked Questions
How does xiaohongshu-mcp handle missing or invalid image files?
The system performs strict pre-validation using os.Stat on each provided path before any browser interaction occurs. Files that do not exist are logged as warnings and skipped entirely, ensuring that only valid, accessible files are passed to the Rod browser's SetFiles method. This prevents the upload sequence from stalling due to broken file references.
Why does the upload process use different CSS selectors for the first versus subsequent images?
Xiaohongshu's web interface presents different DOM states depending on whether any images have been uploaded yet. The first upload requires clicking the visible "Add Image" button (selector .upload-input), which reveals the hidden file input. After the first image is uploaded, the file input remains in the DOM and can be targeted directly with input[type="file"] for subsequent uploads. This selector differentiation ensures reliable element detection across the upload sequence.
What happens if an image upload takes longer than 60 seconds?
The waitForUploadComplete function implements a hard timeout of 60 seconds (defined as maxWaitTime := 60 * time.Second). If the expected number of preview thumbnails (.img-preview-area .pr) does not appear within this window, the function returns an error wrapped with the message indicating which image timed out and suggesting checks for network connectivity or image file size. This error propagates up to halt the publishing process, preventing submission of incomplete posts.
Can the upload mechanism handle video files as well?
While the primary uploadImages function in publish.go specifically handles image uploads, the repository contains a separate implementation in xiaohongshu/publish_video.go that follows a similar pattern for video content. Both implementations use Rod's SetFiles method and DOM polling strategies, but they target different upload widgets and completion indicators appropriate to their respective media types. The architecture allows for shared browser session management while keeping media-specific logic separated.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →