Comprehensive Java SDK for WeChat MiniApp development with complete platform integration
—
Media file management, content security, and content moderation services for images, videos, audio files, and other media types in WeChat MiniApp applications.
Core media operations for uploading, managing, and processing media files.
public interface WxMaMediaService {
// Media Upload
WxMediaUploadResult uploadMedia(String type, File media) throws WxErrorException;
WxMediaUploadResult uploadMedia(String type, InputStream inputStream, String fileName) throws WxErrorException;
// Media Download
File downloadMedia(String mediaId, File file) throws WxErrorException;
InputStream downloadMedia(String mediaId) throws WxErrorException;
// Temporary Media (3 days retention)
WxMediaUploadResult uploadTempMedia(String type, File media) throws WxErrorException;
WxMediaUploadResult uploadTempMedia(String type, InputStream inputStream, String fileName) throws WxErrorException;
// Image Upload (permanent)
WxMediaUploadResult uploadImg(File media) throws WxErrorException;
WxMediaUploadResult uploadImg(InputStream inputStream, String fileName) throws WxErrorException;
}Content security and moderation operations for text, images, and other media content.
public interface WxMaSecurityService {
// Text Security Check
boolean checkText(String content) throws WxErrorException;
boolean checkText(String content, Integer scene, Integer version) throws WxErrorException;
// Image Security Check
boolean checkImage(File imageFile) throws WxErrorException;
boolean checkImage(String mediaId) throws WxErrorException;
boolean checkImage(InputStream inputStream, String fileName) throws WxErrorException;
// Audio Security Check
boolean checkAudio(File audioFile) throws WxErrorException;
boolean checkAudio(String mediaId) throws WxErrorException;
// User Risk Assessment
WxMaUserRiskRank getUserRiskRank(String openid, Integer scene) throws WxErrorException;
}Data models for media upload operations and responses.
public class WxMediaUploadResult implements Serializable {
private String type; // Media type (image, voice, video, thumb)
private String mediaId; // Media ID for subsequent operations
private Long createdAt; // Upload timestamp
private String url; // Media URL (for permanent images)
// Getters and setters
public String getType();
public void setType(String type);
public String getMediaId();
public void setMediaId(String mediaId);
public Long getCreatedAt();
public void setCreatedAt(Long createdAt);
public String getUrl();
public void setUrl(String url);
// Utility methods
public Date getCreatedAtAsDate();
public static WxMediaUploadResult fromJson(String json);
public String toJson();
}Data models for content security and user risk assessment.
public class WxMaSecurityCheckResult implements Serializable {
private Integer errCode; // Error code (0: pass, 87014: risky content)
private String errMsg; // Error message
private String traceId; // Trace ID for tracking
private SecurityResult result; // Detailed security check result
public static class SecurityResult {
private String suggest; // Suggestion (pass, review, risky)
private String label; // Risk label (if any)
public String getSuggest();
public void setSuggest(String suggest);
public String getLabel();
public void setLabel(String label);
}
// Getters and setters
public Integer getErrCode();
public void setErrCode(Integer errCode);
public String getErrMsg();
public void setErrMsg(String errMsg);
public String getTraceId();
public void setTraceId(String traceId);
public SecurityResult getResult();
public void setResult(SecurityResult result);
// Convenience methods
public boolean isPassed();
public boolean isRisky();
public boolean needsReview();
}
public class WxMaUserRiskRank implements Serializable {
private Integer riskRank; // Risk rank (0: normal, 1-4: increasing risk levels)
private String openid; // User OpenID
private Long timestamp; // Assessment timestamp
// Getters and setters
public Integer getRiskRank();
public void setRiskRank(Integer riskRank);
public String getOpenid();
public void setOpenid(String openid);
public Long getTimestamp();
public void setTimestamp(Long timestamp);
// Convenience methods
public boolean isHighRisk();
public boolean isNormalRisk();
public String getRiskLevel();
}Constants for different media types and security check parameters.
public class WxMaMediaConstants {
// Media Types
public static final String IMAGE = "image";
public static final String VOICE = "voice";
public static final String VIDEO = "video";
public static final String THUMB = "thumb";
// Security Check Scenes
public static final Integer SCENE_SOCIAL = 1; // Social scenarios
public static final Integer SCENE_COMMENT = 2; // Comment scenarios
public static final Integer SCENE_FORUM = 3; // Forum scenarios
public static final Integer SCENE_OPEN_API = 4; // Open API scenarios
// Security Check Versions
public static final Integer VERSION_LATEST = 2; // Latest version
// Risk Levels
public static final Integer RISK_NORMAL = 0; // Normal user
public static final Integer RISK_LOW = 1; // Low risk
public static final Integer RISK_MEDIUM = 2; // Medium risk
public static final Integer RISK_HIGH = 3; // High risk
public static final Integer RISK_VERY_HIGH = 4; // Very high risk
}@Service
public class MediaUploadService {
@Autowired
private WxMaService wxMaService;
public String uploadUserAvatar(File avatarFile) {
try {
// Upload as permanent image (no expiration)
WxMediaUploadResult result = wxMaService.getMediaService()
.uploadImg(avatarFile);
String mediaUrl = result.getUrl();
logger.info("Avatar uploaded successfully: {}", mediaUrl);
return mediaUrl;
} catch (WxErrorException e) {
logger.error("Avatar upload failed: {}", e.getMessage());
throw new MediaUploadException("Failed to upload avatar", e);
}
}
public String uploadTemporaryImage(MultipartFile file) {
try {
// Upload as temporary media (3 days retention)
WxMediaUploadResult result = wxMaService.getMediaService()
.uploadTempMedia(WxMaMediaConstants.IMAGE,
file.getInputStream(),
file.getOriginalFilename());
String mediaId = result.getMediaId();
logger.info("Temporary image uploaded: {}", mediaId);
return mediaId;
} catch (WxErrorException | IOException e) {
logger.error("Temporary image upload failed: {}", e.getMessage());
throw new MediaUploadException("Failed to upload temporary image", e);
}
}
public String uploadProductImage(File productImage) {
try {
// First check image content for security
boolean isSecure = wxMaService.getSecurityService()
.checkImage(productImage);
if (!isSecure) {
throw new SecurityCheckException("Image contains inappropriate content");
}
// Upload if security check passes
WxMediaUploadResult result = wxMaService.getMediaService()
.uploadImg(productImage);
return result.getUrl();
} catch (WxErrorException e) {
logger.error("Product image upload failed: {}", e.getMessage());
throw new MediaUploadException("Failed to upload product image", e);
}
}
}public class MultiMediaService {
public String uploadVoiceMessage(File voiceFile) {
try {
// Check audio content first
boolean isSecure = wxMaService.getSecurityService()
.checkAudio(voiceFile);
if (!isSecure) {
throw new SecurityCheckException("Audio contains inappropriate content");
}
// Upload voice as temporary media
WxMediaUploadResult result = wxMaService.getMediaService()
.uploadTempMedia(WxMaMediaConstants.VOICE, voiceFile);
String mediaId = result.getMediaId();
logger.info("Voice message uploaded: {}", mediaId);
return mediaId;
} catch (WxErrorException e) {
logger.error("Voice upload failed: {}", e.getMessage());
throw new MediaUploadException("Failed to upload voice message", e);
}
}
public String uploadVideo(File videoFile) {
try {
// Upload video as temporary media
WxMediaUploadResult result = wxMaService.getMediaService()
.uploadTempMedia(WxMaMediaConstants.VIDEO, videoFile);
String mediaId = result.getMediaId();
logger.info("Video uploaded: {}", mediaId);
return mediaId;
} catch (WxErrorException e) {
logger.error("Video upload failed: {}", e.getMessage());
throw new MediaUploadException("Failed to upload video", e);
}
}
public String uploadThumbnail(File thumbnailFile) {
try {
// Upload thumbnail for video
WxMediaUploadResult result = wxMaService.getMediaService()
.uploadTempMedia(WxMaMediaConstants.THUMB, thumbnailFile);
return result.getMediaId();
} catch (WxErrorException e) {
logger.error("Thumbnail upload failed: {}", e.getMessage());
throw new MediaUploadException("Failed to upload thumbnail", e);
}
}
}public class MediaDownloadService {
public File downloadMedia(String mediaId, String targetDirectory) {
try {
// Create target file
File targetFile = new File(targetDirectory, "media_" + mediaId + ".dat");
// Download media
File downloadedFile = wxMaService.getMediaService()
.downloadMedia(mediaId, targetFile);
logger.info("Media downloaded: {} -> {}", mediaId, downloadedFile.getPath());
return downloadedFile;
} catch (WxErrorException e) {
logger.error("Media download failed for {}: {}", mediaId, e.getMessage());
throw new MediaDownloadException("Failed to download media", e);
}
}
public byte[] downloadMediaAsBytes(String mediaId) {
try (InputStream inputStream = wxMaService.getMediaService().downloadMedia(mediaId)) {
return IOUtils.toByteArray(inputStream);
} catch (WxErrorException | IOException e) {
logger.error("Media download as bytes failed for {}: {}", mediaId, e.getMessage());
throw new MediaDownloadException("Failed to download media as bytes", e);
}
}
@Async
public CompletableFuture<String> processAndUpload(File originalFile, ImageProcessingOptions options) {
return CompletableFuture.supplyAsync(() -> {
try {
// Process image (resize, crop, etc.)
File processedFile = imageProcessor.process(originalFile, options);
// Upload processed image
WxMediaUploadResult result = wxMaService.getMediaService()
.uploadImg(processedFile);
// Clean up temporary file
processedFile.delete();
return result.getUrl();
} catch (Exception e) {
logger.error("Async media processing failed: {}", e.getMessage());
return null;
}
});
}
}@Service
public class ContentModerationService {
public boolean checkTextContent(String content, ContentScene scene) {
try {
Integer sceneCode = getSceneCode(scene);
boolean isSecure = wxMaService.getSecurityService()
.checkText(content, sceneCode, WxMaMediaConstants.VERSION_LATEST);
if (!isSecure) {
logger.warn("Text content failed security check: {}",
content.substring(0, Math.min(50, content.length())));
}
return isSecure;
} catch (WxErrorException e) {
logger.error("Text security check failed: {}", e.getMessage());
// Default to rejecting on error for safety
return false;
}
}
public ContentModerationResult moderateUserComment(String comment, String openid) {
try {
// Check text content
boolean textSecure = wxMaService.getSecurityService()
.checkText(comment, WxMaMediaConstants.SCENE_COMMENT,
WxMaMediaConstants.VERSION_LATEST);
// Check user risk level
WxMaUserRiskRank userRisk = wxMaService.getSecurityService()
.getUserRiskRank(openid, WxMaMediaConstants.SCENE_COMMENT);
ContentModerationResult result = new ContentModerationResult();
result.setTextSecure(textSecure);
result.setUserRiskLevel(userRisk.getRiskRank());
result.setApproved(textSecure && !userRisk.isHighRisk());
if (result.isApproved()) {
result.setAction("approve");
} else if (!textSecure) {
result.setAction("reject_content");
} else if (userRisk.isHighRisk()) {
result.setAction("manual_review");
}
return result;
} catch (WxErrorException e) {
logger.error("Comment moderation failed: {}", e.getMessage());
// Default to manual review on error
ContentModerationResult result = new ContentModerationResult();
result.setAction("manual_review");
result.setApproved(false);
return result;
}
}
private Integer getSceneCode(ContentScene scene) {
switch (scene) {
case SOCIAL: return WxMaMediaConstants.SCENE_SOCIAL;
case COMMENT: return WxMaMediaConstants.SCENE_COMMENT;
case FORUM: return WxMaMediaConstants.SCENE_FORUM;
case OPEN_API: return WxMaMediaConstants.SCENE_OPEN_API;
default: return WxMaMediaConstants.SCENE_SOCIAL;
}
}
}public class ImageModerationService {
public boolean checkUploadedImage(MultipartFile imageFile) {
try {
// Check image content
boolean isSecure = wxMaService.getSecurityService()
.checkImage(imageFile.getInputStream(), imageFile.getOriginalFilename());
if (!isSecure) {
logger.warn("Uploaded image failed security check: {}",
imageFile.getOriginalFilename());
}
return isSecure;
} catch (WxErrorException | IOException e) {
logger.error("Image security check failed: {}", e.getMessage());
return false;
}
}
public ImageModerationResult moderateUserAvatar(File avatarFile, String openid) {
try {
// Check image content
boolean imageSecure = wxMaService.getSecurityService()
.checkImage(avatarFile);
// Check user risk level
WxMaUserRiskRank userRisk = wxMaService.getSecurityService()
.getUserRiskRank(openid, WxMaMediaConstants.SCENE_SOCIAL);
ImageModerationResult result = new ImageModerationResult();
result.setImageSecure(imageSecure);
result.setUserRiskLevel(userRisk.getRiskRank());
// Decision logic
if (imageSecure && userRisk.getRiskRank() <= WxMaMediaConstants.RISK_LOW) {
result.setApproved(true);
result.setAction("approve");
} else if (!imageSecure) {
result.setApproved(false);
result.setAction("reject_inappropriate");
} else {
result.setApproved(false);
result.setAction("manual_review");
}
return result;
} catch (WxErrorException e) {
logger.error("Avatar moderation failed: {}", e.getMessage());
ImageModerationResult result = new ImageModerationResult();
result.setApproved(false);
result.setAction("error_occurred");
return result;
}
}
@Async
public void batchCheckImages(List<String> mediaIds) {
for (String mediaId : mediaIds) {
try {
boolean isSecure = wxMaService.getSecurityService()
.checkImage(mediaId);
// Update database with security status
mediaSecurityRepository.updateSecurityStatus(mediaId, isSecure);
if (!isSecure) {
// Handle inappropriate content
handleInappropriateMedia(mediaId);
}
// Rate limiting - small delay between checks
Thread.sleep(100);
} catch (WxErrorException | InterruptedException e) {
logger.error("Batch image check failed for {}: {}", mediaId, e.getMessage());
}
}
}
}@Service
public class UserRiskAssessmentService {
public UserRiskProfile assessUserRisk(String openid, RiskAssessmentContext context) {
try {
Integer scene = getSceneFromContext(context);
WxMaUserRiskRank riskResult = wxMaService.getSecurityService()
.getUserRiskRank(openid, scene);
UserRiskProfile profile = new UserRiskProfile();
profile.setOpenid(openid);
profile.setRiskRank(riskResult.getRiskRank());
profile.setRiskLevel(getRiskLevelDescription(riskResult.getRiskRank()));
profile.setAssessmentTime(new Date(riskResult.getTimestamp() * 1000));
profile.setRecommendations(generateRecommendations(riskResult.getRiskRank()));
// Store assessment for future reference
userRiskRepository.save(profile);
return profile;
} catch (WxErrorException e) {
logger.error("User risk assessment failed for {}: {}", openid, e.getMessage());
// Return default safe assessment on error
UserRiskProfile profile = new UserRiskProfile();
profile.setOpenid(openid);
profile.setRiskRank(WxMaMediaConstants.RISK_MEDIUM);
profile.setRiskLevel("Unknown (Assessment Failed)");
profile.setAssessmentTime(new Date());
return profile;
}
}
public boolean shouldAllowOperation(String openid, UserOperation operation) {
try {
Integer scene = getSceneFromOperation(operation);
WxMaUserRiskRank riskResult = wxMaService.getSecurityService()
.getUserRiskRank(openid, scene);
// Define operation permissions based on risk level
switch (operation) {
case POST_COMMENT:
return riskResult.getRiskRank() <= WxMaMediaConstants.RISK_MEDIUM;
case UPLOAD_IMAGE:
return riskResult.getRiskRank() <= WxMaMediaConstants.RISK_HIGH;
case SEND_MESSAGE:
return riskResult.getRiskRank() <= WxMaMediaConstants.RISK_LOW;
case CREATE_CONTENT:
return riskResult.getRiskRank() <= WxMaMediaConstants.RISK_MEDIUM;
default:
return riskResult.getRiskRank() <= WxMaMediaConstants.RISK_LOW;
}
} catch (WxErrorException e) {
logger.error("Risk check failed for {} operation {}: {}",
openid, operation, e.getMessage());
// Default to disallow on error for safety
return false;
}
}
private String getRiskLevelDescription(Integer riskRank) {
switch (riskRank) {
case 0: return "Normal";
case 1: return "Low Risk";
case 2: return "Medium Risk";
case 3: return "High Risk";
case 4: return "Very High Risk";
default: return "Unknown";
}
}
private List<String> generateRecommendations(Integer riskRank) {
List<String> recommendations = new ArrayList<>();
if (riskRank >= WxMaMediaConstants.RISK_MEDIUM) {
recommendations.add("Enable manual review for user-generated content");
recommendations.add("Increase monitoring frequency");
}
if (riskRank >= WxMaMediaConstants.RISK_HIGH) {
recommendations.add("Restrict content creation abilities");
recommendations.add("Require additional verification");
}
if (riskRank >= WxMaMediaConstants.RISK_VERY_HIGH) {
recommendations.add("Consider temporary restrictions");
recommendations.add("Flag for admin review");
}
return recommendations;
}
}@Service
public class MediaProcessingPipeline {
@Async
public CompletableFuture<ProcessedMediaResult> processUploadedMedia(
MultipartFile file, String openid, MediaProcessingOptions options) {
return CompletableFuture.supplyAsync(() -> {
try {
ProcessedMediaResult result = new ProcessedMediaResult();
// Step 1: Basic validation
if (!validateFile(file)) {
result.setSuccess(false);
result.setError("Invalid file format or size");
return result;
}
// Step 2: Security check
boolean isSecure = wxMaService.getSecurityService()
.checkImage(file.getInputStream(), file.getOriginalFilename());
if (!isSecure) {
result.setSuccess(false);
result.setError("Content security check failed");
return result;
}
// Step 3: User risk assessment
WxMaUserRiskRank userRisk = wxMaService.getSecurityService()
.getUserRiskRank(openid, WxMaMediaConstants.SCENE_SOCIAL);
if (userRisk.isHighRisk()) {
result.setSuccess(false);
result.setError("User risk level too high");
return result;
}
// Step 4: Process image if needed
File processedFile = file instanceof File ? (File) file :
convertMultipartToFile(file);
if (options.isResizeRequired()) {
processedFile = imageProcessor.resize(processedFile,
options.getTargetWidth(),
options.getTargetHeight());
}
// Step 5: Upload to WeChat
WxMediaUploadResult uploadResult;
if (options.isPermanent()) {
uploadResult = wxMaService.getMediaService().uploadImg(processedFile);
} else {
uploadResult = wxMaService.getMediaService()
.uploadTempMedia(WxMaMediaConstants.IMAGE, processedFile);
}
// Step 6: Build result
result.setSuccess(true);
result.setMediaId(uploadResult.getMediaId());
result.setMediaUrl(uploadResult.getUrl());
result.setProcessingTime(System.currentTimeMillis() - startTime);
// Clean up temporary files
if (processedFile != file) {
processedFile.delete();
}
return result;
} catch (Exception e) {
logger.error("Media processing pipeline failed: {}", e.getMessage());
ProcessedMediaResult result = new ProcessedMediaResult();
result.setSuccess(false);
result.setError("Processing pipeline error: " + e.getMessage());
return result;
}
});
}
private boolean validateFile(MultipartFile file) {
// Check file size (e.g., max 10MB)
if (file.getSize() > 10 * 1024 * 1024) {
return false;
}
// Check file type
String contentType = file.getContentType();
return contentType != null && contentType.startsWith("image/");
}
}@RestController
@RequestMapping("/api/moderation")
public class ModerationController {
@PostMapping("/check/text")
public ResponseEntity<ModerationResult> checkText(
@RequestBody TextModerationRequest request) {
try {
boolean isSecure = wxMaService.getSecurityService()
.checkText(request.getContent(),
request.getScene(),
WxMaMediaConstants.VERSION_LATEST);
ModerationResult result = new ModerationResult();
result.setSecure(isSecure);
result.setAction(isSecure ? "approve" : "reject");
return ResponseEntity.ok(result);
} catch (WxErrorException e) {
logger.error("Text moderation API failed: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ModerationResult(false, "error", e.getMessage()));
}
}
@PostMapping("/check/image")
public ResponseEntity<ModerationResult> checkImage(
@RequestParam("file") MultipartFile file) {
try {
boolean isSecure = wxMaService.getSecurityService()
.checkImage(file.getInputStream(), file.getOriginalFilename());
ModerationResult result = new ModerationResult();
result.setSecure(isSecure);
result.setAction(isSecure ? "approve" : "reject");
return ResponseEntity.ok(result);
} catch (WxErrorException | IOException e) {
logger.error("Image moderation API failed: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ModerationResult(false, "error", e.getMessage()));
}
}
@GetMapping("/user/{openid}/risk")
public ResponseEntity<UserRiskProfile> getUserRisk(
@PathVariable String openid,
@RequestParam(defaultValue = "1") Integer scene) {
try {
WxMaUserRiskRank riskResult = wxMaService.getSecurityService()
.getUserRiskRank(openid, scene);
UserRiskProfile profile = new UserRiskProfile();
profile.setOpenid(openid);
profile.setRiskRank(riskResult.getRiskRank());
profile.setRiskLevel(getRiskLevelDescription(riskResult.getRiskRank()));
profile.setAssessmentTime(new Date(riskResult.getTimestamp() * 1000));
return ResponseEntity.ok(profile);
} catch (WxErrorException e) {
logger.error("User risk API failed: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}This media and content module provides comprehensive functionality for media file management, content security checks, and user risk assessment with automated moderation capabilities for WeChat MiniApp applications.
Install with Tessl CLI
npx tessl i tessl/maven-com-github-binarywang--weixin-java-miniapp