一、背景在小程序的一些应用场景中,会有语音转文字的需求。原有的做法一般是先通过小程序的录音功能录下语音文件,然后再通过调用语音智能识别WebApi(比如百度云AI平台,科大讯飞平台)将语音文件转成文字信息,以上的做法比较繁琐且用户的体验性较差。为解决此问题,微信直接开放了同声传译的插件,小程序作者可以直接使用该插件进行语音同声传译的开发。此文章将通过前后端整合应用的完整案例完成语音的实时转换,并将语音上传到服务端后台备份。二、同声传译插件介绍微信同声传译由微信智聆语音团队、微信翻译团队与公众平台联合推出的同传开放接口,首期开放语音转文字、文本翻译、语音合成接口,为开发者赋能。1、 微信小程序后台添加插件1、 微信小程序后台添加插件进入微信小程序后台-->进入设置-->第三方设置-->添加插件->搜索同声传译-->完成添加。
2、 微信小程序启用插件2、 微信小程序启用插件在小程序app.json文件中增加插件版本等信息:
"plugins": {
"WechatSI": {
"version": "0.3.3",
"provider": "wx069ba97219f66d99"
}
},
"plugins": {
"WechatSI": {
"version": "0.3.3",
"provider": "wx069ba97219f66d99"
}
},在页面程序文件中引入插件:
/* index.js */

const plugin = requirePlugin("WechatSI")

// 获取**全局唯一**的语音识别管理器**recordRecoManager**
const manager = plugin.getRecordRecognitionManager()
/* index.js */

const plugin = requirePlugin("WechatSI")

// 获取**全局唯一**的语音识别管理器**recordRecoManager**
const manager = plugin.getRecordRecognitionManager()recordRecoManager 对象的方法列表:






方法

参数

说明









start

options

开始识别





stop



结束识别





onStart

callback

正常开始录音识别时会调用此事件





onRecognize

callback

有新的识别内容返回,则会调用此事件





onStop

callback

识别结束事件





onError

callback

识别错误事件








方法

参数

说明





方法

参数

说明

方法参数说明



start

options

开始识别





stop



结束识别





onStart

callback

正常开始录音识别时会调用此事件





onRecognize

callback

有新的识别内容返回,则会调用此事件





onStop

callback

识别结束事件





onError

callback

识别错误事件





start

options

开始识别

startoptions开始识别

stop



结束识别

stop结束识别

onStart

callback

正常开始录音识别时会调用此事件

onStartcallback正常开始录音识别时会调用此事件

onRecognize

callback

有新的识别内容返回,则会调用此事件

onRecognizecallback有新的识别内容返回,则会调用此事件

onStop

callback

识别结束事件

onStopcallback识别结束事件

onError

callback

识别错误事件

onErrorcallback识别错误事件官方开发文档:插件的语音识别管理器插件的语音识别管理器三、语音同步转换的前端实现三、语音同步转换的前端实现三、语音同步转换的前端实现1、界面UI与操作1、界面UI与操作UI参考微信官方的DEMO:长按按钮进行录音,松开按钮实时将录音转换为文字。用户可对同步转换的文字进行编辑,同时可将原始语音文件与文字上传后台服务端。2、代码实现2、代码实现语音同步转换的主要代码:
//导入插件
const plugin = requirePlugin("WechatSI");
// 获取**全局唯一**的语音识别管理器**recordRecoManager**
const manager = plugin.getRecordRecognitionManager();

/**
* 加载进行初始化
*/
onLoad: function () {

//获取录音权限

app.getRecordAuth();

//初始化语音识别回调
this.initRecord();
},

...

/**
* 初始化语音识别回调
* 绑定语音播放开始事件
*/
initRecord: function () {
//有新的识别内容返回,则会调用此事件
manager.onRecognize = (res) => {
let currentData = Object.assign({}, this.data.currentTranslate, {
text: res.result,
});
this.setData({
currentTranslate: currentData,
});
this.scrollToNew();
};

// 识别结束事件
manager.onStop = (res) => {
let text = res.result;

console.log(res.tempFilePath);

if (text == "") {
this.showRecordEmptyTip();
return;
}

let lastId = this.data.lastId + 1;

let currentData = Object.assign({}, this.data.currentTranslate, {
text: res.result,
translateText: "正在识别中",
id: lastId,
voicePath: res.tempFilePath,
duration: res.duration
});

this.setData({
currentTranslate: currentData,
recordStatus: 1,
lastId: lastId,
});
//将当前识别内容与语音文件加入列表
this.addRecordFile(currentData, this.data.dialogList.length);
//刷新列表

this.scrollToNew();
};

// 识别错误事件
manager.onError = (res) => {
this.setData({
recording: false,
bottomButtonDisabled: false,
});
};

},

/**
* 按住按钮开始语音识别
*/
streamRecord: function (e) {
let detail = e.detail || {};
let buttonItem = detail.buttonItem || {};
//开始中文录音
manager.start({
lang: buttonItem.lang,
});

this.setData({
recordStatus: 0,
recording: true,
currentTranslate: {
// 当前语音输入内容
create: util.recordTime(new Date()),
text: "正在聆听中",
lfrom: buttonItem.lang,
lto: buttonItem.lto,
},
});
//刷新列表
this.scrollToNew();
},

/**
* 松开按钮结束语音识别
*/
streamRecordEnd: function (e) {
let detail = e.detail || {}; // 自定义组件触发事件时提供的detail对象
let buttonItem = detail.buttonItem || {};

// 防止重复触发stop函数
if (!this.data.recording || this.data.recordStatus != 0) {
console.warn("has finished!");
return;
}

manager.stop();

this.setData({
bottomButtonDisabled: true,
});
},
//导入插件
const plugin = requirePlugin("WechatSI");
// 获取**全局唯一**的语音识别管理器**recordRecoManager**
const manager = plugin.getRecordRecognitionManager();

/**
* 加载进行初始化
*/
onLoad: function () {

//获取录音权限

app.getRecordAuth();

//初始化语音识别回调
this.initRecord();
},

...

/**
* 初始化语音识别回调
* 绑定语音播放开始事件
*/
initRecord: function () {
//有新的识别内容返回,则会调用此事件
manager.onRecognize = (res) => {
let currentData = Object.assign({}, this.data.currentTranslate, {
text: res.result,
});
this.setData({
currentTranslate: currentData,
});
this.scrollToNew();
};

// 识别结束事件
manager.onStop = (res) => {
let text = res.result;

console.log(res.tempFilePath);

if (text == "") {
this.showRecordEmptyTip();
return;
}

let lastId = this.data.lastId + 1;

let currentData = Object.assign({}, this.data.currentTranslate, {
text: res.result,
translateText: "正在识别中",
id: lastId,
voicePath: res.tempFilePath,
duration: res.duration
});

this.setData({
currentTranslate: currentData,
recordStatus: 1,
lastId: lastId,
});
//将当前识别内容与语音文件加入列表
this.addRecordFile(currentData, this.data.dialogList.length);
//刷新列表

this.scrollToNew();
};

// 识别错误事件
manager.onError = (res) => {
this.setData({
recording: false,
bottomButtonDisabled: false,
});
};

},

/**
* 按住按钮开始语音识别
*/
streamRecord: function (e) {
let detail = e.detail || {};
let buttonItem = detail.buttonItem || {};
//开始中文录音
manager.start({
lang: buttonItem.lang,
});

this.setData({
recordStatus: 0,
recording: true,
currentTranslate: {
// 当前语音输入内容
create: util.recordTime(new Date()),
text: "正在聆听中",
lfrom: buttonItem.lang,
lto: buttonItem.lto,
},
});
//刷新列表
this.scrollToNew();
},

/**
* 松开按钮结束语音识别
*/
streamRecordEnd: function (e) {
let detail = e.detail || {}; // 自定义组件触发事件时提供的detail对象
let buttonItem = detail.buttonItem || {};

// 防止重复触发stop函数
if (!this.data.recording || this.data.recordStatus != 0) {
console.warn("has finished!");
return;
}

manager.stop();

this.setData({
bottomButtonDisabled: true,
});
},编辑识别文字并完上传的主要代码:
/**
* 页面的初始数据
*/
data: {
edit_text_max: 200,
remain_length: 200,
edit_text: "",
is_focus: false,
tips: "",
index: -1,
voicePath: "",

},

/**
* 加载初始化
*/
onLoad: function (options) {
//根据传入的文字内容填充编辑框
this.setEditText(options.content)

this.setData({
index: index,
oldText:options.content,
voicePath: options.voicePath
})

},

/**
* 编辑文字
*/
editInput: function (event) {
console.log(event)
if (event.detail.value.length > this.getEditTextMax()) {

} else {
this.data.edit_text = event.detail.value
this.updateRemainLength(this.data.edit_text)
}
},

/**
* 上传文字与语音文件
*/
editConfirm: function (event) {
let json=this.data.edit_text
//调用微信上传文件api将信息上传至服务端webApi
wx.uploadFile({
url: api.wxFileUploadUrl,
filePath: this.data.voicePath,
name: "file",
header: {
Authorization: wx.getStorageSync("loginFlag"),
"Content-Type": "multipart/form-data",
},
formData: {
openId: app.globalData.userInfo.openId,
realName: "语音文件",
json: JSON.stringify(json),
},
success: (result) => {
console.log("success:", result);
if (result.statusCode == "200") {

let data = JSON.parse(result.data);

console.log("data", data);

if (data.success == true) {

let module = data.module;

console.log("module", module);

app.showInfo("上传成功");


setTimeout( ()=>{

wx.navigateBack();

}, 2000)



} else {

app.showInfo("异常错误" + data.errMsg + ",请重新进入");

wx.navigateTo({

url: "/pages/index/index",

});

}
} else {

app.showInfo("访问后台异常,重新进入系统");

wx.navigateTo({

url: "/pages/index/index",

});
}
},
fail: (result) => {
console.log("fail", result);
wx.navigateTo({

url: "/pages/index/index",
});
},
complete: () => {},
});

},
/**
* 页面的初始数据
*/
data: {
edit_text_max: 200,
remain_length: 200,
edit_text: "",
is_focus: false,
tips: "",
index: -1,
voicePath: "",

},

/**
* 加载初始化
*/
onLoad: function (options) {
//根据传入的文字内容填充编辑框
this.setEditText(options.content)

this.setData({
index: index,
oldText:options.content,
voicePath: options.voicePath
})

},

/**
* 编辑文字
*/
editInput: function (event) {
console.log(event)
if (event.detail.value.length > this.getEditTextMax()) {

} else {
this.data.edit_text = event.detail.value
this.updateRemainLength(this.data.edit_text)
}
},

/**
* 上传文字与语音文件
*/
editConfirm: function (event) {
let json=this.data.edit_text
//调用微信上传文件api将信息上传至服务端webApi
wx.uploadFile({
url: api.wxFileUploadUrl,
filePath: this.data.voicePath,
name: "file",
header: {
Authorization: wx.getStorageSync("loginFlag"),
"Content-Type": "multipart/form-data",
},
formData: {
openId: app.globalData.userInfo.openId,
realName: "语音文件",
json: JSON.stringify(json),
},
success: (result) => {
console.log("success:", result);
if (result.statusCode == "200") {

let data = JSON.parse(result.data);

console.log("data", data);

if (data.success == true) {

let module = data.module;

console.log("module", module);

app.showInfo("上传成功");


setTimeout( ()=>{

wx.navigateBack();

}, 2000)



} else {

app.showInfo("异常错误" + data.errMsg + ",请重新进入");

wx.navigateTo({

url: "/pages/index/index",

});

}
} else {

app.showInfo("访问后台异常,重新进入系统");

wx.navigateTo({

url: "/pages/index/index",

});
}
},
fail: (result) => {
console.log("fail", result);
wx.navigateTo({

url: "/pages/index/index",
});
},
complete: () => {},
});

},四、后端SpringBoot实现语音文件上传webApi1、SpringBoot项目API相关结构树1、SpringBoot项目API相关结构树2、文件上传工具类的实现2、文件上传工具类的实现tools工具类包中主要存文件通用的文件上传工具类,该工具类会将文件上传至配置指定的文件夹下,并将文件信息写入upload_file表中。

文件信息实体类:与数据库中表upload_file对应;

文件存储仓库类:通过Spring Data JPA接口实现数据的CRUD;

文件上传工具接口:对外统一封装文件上传方法;

文件上传工具实现类:实现文件上传方法接口。
文件信息实体类:与数据库中表upload_file对应;文件存储仓库类:通过Spring Data JPA接口实现数据的CRUD;文件上传工具接口:对外统一封装文件上传方法;文件上传工具实现类:实现文件上传方法接口。文件信息实体类:UploadFile.java
/**
* 文件信息表
*
* @author zhuhuix
* @date 2021-04-20
*/
@Entity
@Getter
@Setter
@Table(name = "upload_file")
public class UploadFile {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@NotNull(groups = Update.class)
private Long id;

/**
* 文件实际名称
*/
@Column(name = "real_name")
private String realName;

/**
* 文件名
*/
@NotNull
@Column(name = "file_name")
private String fileName;

/**
* 文件主名称
*/
@NotNull
@Column(name = "primary_name")
private String primaryName;

/**
* 文件扩展名
*/
@NotNull
private String extension;

/**
* 存放路径
*/
@NotNull
private String path;

/**
* 文件类型
*/
private String type;

/**
* 文件大小
*/
private Long size;

/**
* 上传人
*/
private String uploader;

@JsonIgnore
@Column(name = "create_time")
@CreationTimestamp
private Timestamp createTime;

public UploadFile(String realName, @NotNull String fileName, @NotNull String primaryName, @NotNull String extension, @NotNull String path, String type, Long size, String uploader) {
this.realName = realName;
this.fileName = fileName;
this.primaryName = primaryName;
this.extension = extension;
this.path = path;
this.type = type;
this.size = size;
this.uploader = uploader;
}

@Override
public String toString() {
return "UploadFile{" +

"fileName='" + fileName + '\'' +

", uploader='" + uploader + '\'' +

", createTime=" + createTime +

'}';
}
}
/**
* 文件信息表
*
* @author zhuhuix
* @date 2021-04-20
*/
@Entity
@Getter
@Setter
@Table(name = "upload_file")
public class UploadFile {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@NotNull(groups = Update.class)
private Long id;

/**
* 文件实际名称
*/
@Column(name = "real_name")
private String realName;

/**
* 文件名
*/
@NotNull
@Column(name = "file_name")
private String fileName;

/**
* 文件主名称
*/
@NotNull
@Column(name = "primary_name")
private String primaryName;

/**
* 文件扩展名
*/
@NotNull
private String extension;

/**
* 存放路径
*/
@NotNull
private String path;

/**
* 文件类型
*/
private String type;

/**
* 文件大小
*/
private Long size;

/**
* 上传人
*/
private String uploader;

@JsonIgnore
@Column(name = "create_time")
@CreationTimestamp
private Timestamp createTime;

public UploadFile(String realName, @NotNull String fileName, @NotNull String primaryName, @NotNull String extension, @NotNull String path, String type, Long size, String uploader) {
this.realName = realName;
this.fileName = fileName;
this.primaryName = primaryName;
this.extension = extension;
this.path = path;
this.type = type;
this.size = size;
this.uploader = uploader;
}

@Override
public String toString() {
return "UploadFile{" +

"fileName='" + fileName + '\'' +

", uploader='" + uploader + '\'' +

", createTime=" + createTime +

'}';
}
}文件存储仓库类:UploadFileRepository.java
/**
* 上传文件DAO接口层
*
* @author zhuhuix
* @date 2021-04-03
*/
public interface UploadFileRepository extends JpaRepository, JpaSpecificationExecutor {
//该接口继承JpaRepository及CrudRepository接口,已实现了如findById,save,delete等CRUD方法
}
/**
* 上传文件DAO接口层
*
* @author zhuhuix
* @date 2021-04-03
*/
public interface UploadFileRepository extends JpaRepository, JpaSpecificationExecutor {
//该接口继承JpaRepository及CrudRepository接口,已实现了如findById,save,delete等CRUD方法
}UploadFileRepository 接口继承JpaRepository及CrudRepository接口,已实现了如findById,save,delete等CRUD方法文件上传工具接口:UploadFileTool.java
/**
* 文件上传接口定义
*
* @author zhuhuix
* @date 2021-04-20
*/
public interface UploadFileTool {

/**
* 文件上传
* @param multipartFile 文件
* @return 上传信息
*/
UploadFile upload(String uploader,String realName,MultipartFile multipartFile);
}
/**
* 文件上传接口定义
*
* @author zhuhuix
* @date 2021-04-20
*/
public interface UploadFileTool {

/**
* 文件上传
* @param multipartFile 文件
* @return 上传信息
*/
UploadFile upload(String uploader,String realName,MultipartFile multipartFile);
}文件上传工具实现类:UploadFileToolImpl.java
/**
* 文件上传实现类
*
* @author zhuhuix
* @date 2021-04-20
*/
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class UploadFileToolImpl implements UploadFileTool {

private final UploadFileRepository uploadFileRepository;

@Value("${uploadFile.path}")
private String path;

@Value("${uploadFile.maxSize}")
private long maxSize;

public UploadFileToolImpl(UploadFileRepository uploadFileRepository) {
this.uploadFileRepository = uploadFileRepository;
}

@Override
@Transactional(rollbackFor = Exception.class)
public UploadFile upload(String uploader, String realName, MultipartFile multipartFile) {
//检查文件大小
if (multipartFile.getSize() > maxSize * Constant.MB) {

throw new RuntimeException("超出文件上传大小限制" + maxSize + "MB");
}
//获取上传文件的主文件名与扩展名
String primaryName = FileUtil.mainName(multipartFile.getOriginalFilename());
String extension = FileUtil.extName(multipartFile.getOriginalFilename());
//根据文件扩展名得到文件类型
String type = getFileType(extension);
//给上传的文件加上时间戳
LocalDateTime date = LocalDateTime.now();
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyyMMddhhmmssS");
String nowStr = "-" + date.format(format);
String fileName = primaryName + nowStr + "." + extension;

try {

String filePath = path + type + File.separator + fileName;

File dest = new File(filePath).getCanonicalFile();

if (!dest.getParentFile().exists()) {

dest.getParentFile().mkdirs();

}

multipartFile.transferTo(dest);

if (ObjectUtil.isNull(dest)) {

throw new RuntimeException("上传文件失败");

}


UploadFile uploadFile = new UploadFile(realName, fileName, primaryName, extension, dest.getPath(), type, multipartFile.getSize(), uploader);

return uploadFileRepository.save(uploadFile);

} catch (Exception e) {

e.printStackTrace();

throw new RuntimeException(e.getMessage());
}

}

/**
* 根据文件扩展名给文件类型
*
* @param extension 文件扩展名
* @return 文件类型
*/
private static String getFileType(String extension) {
String document = "txt doc pdf ppt pps xlsx xls docx csv";
String music = "mp3 wav wma mpa ram ra aac aif m4a";
String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg";
String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg";
if (image.contains(extension)) {

return "image";
} else if (document.contains(extension)) {

return "document";
} else if (music.contains(extension)) {

return "music";
} else if (video.contains(extension)) {

return "video";
} else {

return "other";
}
}
}
/**
* 文件上传实现类
*
* @author zhuhuix
* @date 2021-04-20
*/
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class UploadFileToolImpl implements UploadFileTool {

private final UploadFileRepository uploadFileRepository;

@Value("${uploadFile.path}")
private String path;

@Value("${uploadFile.maxSize}")
private long maxSize;

public UploadFileToolImpl(UploadFileRepository uploadFileRepository) {
this.uploadFileRepository = uploadFileRepository;
}

@Override
@Transactional(rollbackFor = Exception.class)
public UploadFile upload(String uploader, String realName, MultipartFile multipartFile) {
//检查文件大小
if (multipartFile.getSize() > maxSize * Constant.MB) {

throw new RuntimeException("超出文件上传大小限制" + maxSize + "MB");
}
//获取上传文件的主文件名与扩展名
String primaryName = FileUtil.mainName(multipartFile.getOriginalFilename());
String extension = FileUtil.extName(multipartFile.getOriginalFilename());
//根据文件扩展名得到文件类型
String type = getFileType(extension);
//给上传的文件加上时间戳
LocalDateTime date = LocalDateTime.now();
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyyMMddhhmmssS");
String nowStr = "-" + date.format(format);
String fileName = primaryName + nowStr + "." + extension;

try {

String filePath = path + type + File.separator + fileName;

File dest = new File(filePath).getCanonicalFile();

if (!dest.getParentFile().exists()) {

dest.getParentFile().mkdirs();

}

multipartFile.transferTo(dest);

if (ObjectUtil.isNull(dest)) {

throw new RuntimeException("上传文件失败");

}


UploadFile uploadFile = new UploadFile(realName, fileName, primaryName, extension, dest.getPath(), type, multipartFile.getSize(), uploader);

return uploadFileRepository.save(uploadFile);

} catch (Exception e) {

e.printStackTrace();

throw new RuntimeException(e.getMessage());
}

}

/**
* 根据文件扩展名给文件类型
*
* @param extension 文件扩展名
* @return 文件类型
*/
private static String getFileType(String extension) {
String document = "txt doc pdf ppt pps xlsx xls docx csv";
String music = "mp3 wav wma mpa ram ra aac aif m4a";
String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg";
String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg";
if (image.contains(extension)) {

return "image";
} else if (document.contains(extension)) {

return "document";
} else if (music.contains(extension)) {

return "music";
} else if (video.contains(extension)) {

return "video";
} else {

return "other";
}
}
}注意,该程序代码中用到了@Value注解获取配置文件中的uploadFile.path及uploadFile.maxsize参数,一般在项目静态配置文件中按如下书写(yml配置文件)。
# 测试环境文件存储路径
uploadFile:
path: C:\startup\file\
# 文件大小 /M
maxSize: 50
# 测试环境文件存储路径
uploadFile:
path: C:\startup\file\
# 文件大小 /M
maxSize: 503、小程序上传文件接口的实现wx-miniprogram包定义了小程序CRM webApi的接口,小程序调用webApi实现文件的上传及其他功能。

微信小程序 webApi:对外提供小程序上传文件webApi;

微信小程序服务接口:封装小程序上传文件服务接口;

微信小程序服务实现:小程序上传文件服务的实现,该服务实现中会调用tools包中的UploadFile接口进行文件的上传。
微信小程序 webApi:对外提供小程序上传文件webApi;微信小程序服务接口:封装小程序上传文件服务接口;微信小程序服务实现:小程序上传文件服务的实现,该服务实现中会调用tools包中的UploadFile接口进行文件的上传。微信小程序CRM webApi:WxMiniCrmController.java
/**
* 微信小程序Crm webApi
*
* @author zhuhuix
* @date 2021-03-30
*/
@Slf4j
@RestController
@RequestMapping("/api/wx-mini")
@Api(tags = "微信小程序Crm接口")
public class WxMiniCrmController {

private final WxMiniCrm wxMiniCrm;

public WxMiniCrmController(WxMiniCrm wxMiniCrm) {
this.wxMiniCrm = wxMiniCrm;
}

@ApiOperation(value = "微信小程序端上传文件")
@PostMapping(value = "/fileUpload")
public ResponseEntity fileUpload(HttpServletRequest request) {
MultipartHttpServletRequest req = (MultipartHttpServletRequest) request;

MultipartFile multipartFile = req.getFile("file");
String openId = req.getParameter("openId");
String realName = req.getParameter("realName");
String json = req.getParameter("json");

return ResponseEntity.ok(wxMiniCrm.uploadFile(json, openId,realName, multipartFile));

}
}
/**
* 微信小程序Crm webApi
*
* @author zhuhuix
* @date 2021-03-30
*/
@Slf4j
@RestController
@RequestMapping("/api/wx-mini")
@Api(tags = "微信小程序Crm接口")
public class WxMiniCrmController {

private final WxMiniCrm wxMiniCrm;

public WxMiniCrmController(WxMiniCrm wxMiniCrm) {
this.wxMiniCrm = wxMiniCrm;
}

@ApiOperation(value = "微信小程序端上传文件")
@PostMapping(value = "/fileUpload")
public ResponseEntity fileUpload(HttpServletRequest request) {
MultipartHttpServletRequest req = (MultipartHttpServletRequest) request;

MultipartFile multipartFile = req.getFile("file");
String openId = req.getParameter("openId");
String realName = req.getParameter("realName");
String json = req.getParameter("json");

return ResponseEntity.ok(wxMiniCrm.uploadFile(json, openId,realName, multipartFile));

}
}微信小程序CRM服务接口:WxMiniCrm.java
/**
* 微信小程序CRM服务接口定义
*
* @author zhuhuix
* @date 2021-04-20
*/
public interface WxMiniCrm {

/**
* 将微信小程序传入的json对象写入数据库,并同时将文件上传至服务端
*
* @param json
微信端传入json对象
* @param openId 上传人
* @param realName 文件实际名称
* @param multipartFile 上传文件
* @return 返回上传信息
*/
Result uploadFile(String json, String openId, String realName,MultipartFile multipartFile);
}
/**
* 微信小程序CRM服务接口定义
*
* @author zhuhuix
* @date 2021-04-20
*/
public interface WxMiniCrm {

/**
* 将微信小程序传入的json对象写入数据库,并同时将文件上传至服务端
*
* @param json
微信端传入json对象
* @param openId 上传人
* @param realName 文件实际名称
* @param multipartFile 上传文件
* @return 返回上传信息
*/
Result uploadFile(String json, String openId, String realName,MultipartFile multipartFile);
}微信小程序CRM服务实现:WxMiniCrmImpl.java
/**
* 微信小程序CRM实现类
*
* @author zhuhuix
* @date 2021-04-20
*/
@Slf4j
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class WxMiniCrmImpl implements WxMiniCrm {

private final UploadFileTool uploadFileTool;

public WxMiniCrmImpl(UploadFileTool uploadFileTool) {
this.uploadFileTool = uploadFileTool;
}

@Override
@Transactional(rollbackFor = Exception.class)
public Result uploadFile(String json, String openId,String realName, MultipartFile multipartFile) {
return new Result().ok(uploadFileTool.upload(openId,realName, multipartFile));
}
}
/**
* 微信小程序CRM实现类
*
* @author zhuhuix
* @date 2021-04-20
*/
@Slf4j
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class WxMiniCrmImpl implements WxMiniCrm {

private final UploadFileTool uploadFileTool;

public WxMiniCrmImpl(UploadFileTool uploadFileTool) {
this.uploadFileTool = uploadFileTool;
}

@Override
@Transactional(rollbackFor = Exception.class)
public Result uploadFile(String json, String openId,String realName, MultipartFile multipartFile) {
return new Result().ok(uploadFileTool.upload(openId,realName, multipartFile));
}
}4、小程序上传文件接口的查看4、小程序上传文件接口的查看访问Swagger2可查看该接口,Swagger2与SpringBoot的集成可参考SpringBoot JWT认证机制项目集成Swagger2SpringBoot JWT认证机制项目集成Swagger2五、实际测试语音测试正常上传文件至后台:上传的日志信息查看:总结总结总结