一:先看效果

AR

二: 整体思路以及用到的第三方图像识别算法

1.调用微信小程序的相机功能(主要使用实时帧数据,已附链接);

CameraFrameListener | 微信开放文档

2.  将获取到的相机实时帧数据转成base64.调取第三方图像识别算法;

Docs

该文档里边有详细的介绍怎么使用去训练我们要识别的图片或者物体,如果有问题可以随时私信我!

3.第三方识别接口识别成功以后会告诉前端扫描的是什么,以便我们去判断该播放哪一个IP人物形象的序列帧动画;

4.提前准备好我们要用到的IP人物资源,我这里是用的序列帧动画(一张张帧照片拼接起来一个人物动画),这些资源建议放在服务器上;

5.创建画布,通过小程序的canvas对所有帧动画进行渲染以及播放;

三:附上全部代码

1.index.wxml

<view class="content"><block wx:if="{{!isShowCamera}}"><view class="scan" bindtap="open"><view class="pup"><view class="pupTitle">活动规则介绍</view><view class="pupbox"><view class="top"><view class="list"><image mode="aspectFit" src="../../img/1.png"></image></view><view class="list"><image mode="aspectFit" src="../../img/2.png"></image></view><view class="list"><image mode="aspectFit" src="../../img/3.png"></image></view><view class="list"><image mode="aspectFit" src="../../img/4.png"></image></view><view class="list"><image mode="aspectFit" src="../../img/5.png"></image></view><view class="list"><image mode="aspectFit" src="../../img/6.png"></image></view></view><view><view class="pupbot">纸上的人物竟然还能动起来?</view><view class="pupbot">不信?!那就对着人物形象扫一扫</view><view class="pupbot">解锁更多精彩吧</view></view></view></view><button>知道啦</button></view></block><block wx:else><view class="page-section page-section-spacing swiper" style="height: 100vh;"><swiper easing-function="easeOutCubic" bindchange="swiperChange" style="height: 100vh;" vertical="{{true}}" autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}"><block><swiper-item style="height: 100vh;"><block><view class="mask" style="height: 100vh;background: transparent;"><camera class="camera" device-position="back" flash="off" frame-size="medium" bindinitdone="bindinitdone"><cover-view hidden="{{!show}}" class='scan-animation' animation="{{animation}}"><cover-image src="../../img/saomiao.png"></cover-image></cover-view><canvas class="prize-frame-canvas" type="2d" id="oneTitleFrameCanvas" style="width: {{width}}rpx;height: {{height}}rpx"></canvas><block><view style="display: flex;justify-content: center;"><text wx:if="{{!textShow}}" class="bottom">扫一扫,开启你的奇妙体验</text><view wx:if="{{textShow}}" class="bottom2"><image class="shanghua" src="../../img/shanghua.png"></image><text>上划发现更多精彩</text></view><view wx:if="{{textShow}}" class="left" hover-class="leftActive" bindtap="toIndex">再玩一次</view></view></block></camera></view></block></swiper-item><swiper-item wx:if="{{textShow}}" catchtouchmove='stopTouchMove' style="height: 100vh;"><block><view class="box" style="background-color: block;"></view></block></swiper-item></block></swiper></view></block></view><canvas canvas-id="myCanvas"></canvas>

2.md5.js(第三方识别接口需要用到)

/* * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message * Digest Algorithm, as defined in RFC 1321. * Version 1.1 Copyright (C) Paul Johnston 1999 - 2002. * Code also contributed by Greg Holt * See http://pajhome.org.uk/site/legal.html for details. */  /* * Add integers, wrapping at 2^32. This uses 16-bit operations internally * to work around bugs in some JS interpreters. */
function safe_add(x, y)
{  var lsw = (x & 0xFFFF) + (y & 0xFFFF)  var msw = (x >> 16) + (y >> 16) + (lsw >> 16)  return (msw << 16) | (lsw & 0xFFFF)
}  /* * Bitwise rotate a 32-bit number to the left. */
function rol(num, cnt)
{  return (num << cnt) | (num >>> (32 - cnt))
}  /* * These functions implement the four basic operations the algorithm uses. */
function cmn(q, a, b, x, s, t)
{  return safe_add(rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b)
}
function ff(a, b, c, d, x, s, t)
{  return cmn((b & c) | ((~b) & d), a, b, x, s, t)
}
function gg(a, b, c, d, x, s, t)
{  return cmn((b & d) | (c & (~d)), a, b, x, s, t)
}
function hh(a, b, c, d, x, s, t)
{  return cmn(b ^ c ^ d, a, b, x, s, t)
}
function ii(a, b, c, d, x, s, t)
{  return cmn(c ^ (b | (~d)), a, b, x, s, t)
}  /* * Calculate the MD5 of an array of little-endian words, producing an array * of little-endian words. */
function coreMD5(x)
{  var a = 1732584193  var b = -271733879  var c = -1732584194  var d = 271733878  for(var i = 0; i < x.length; i += 16)  {  var olda = a  var oldb = b  var oldc = c  var oldd = d  a = ff(a, b, c, d, x[i+ 0], 7 , -680876936)  d = ff(d, a, b, c, x[i+ 1], 12, -389564586)  c = ff(c, d, a, b, x[i+ 2], 17, 606105819)  b = ff(b, c, d, a, x[i+ 3], 22, -1044525330)  a = ff(a, b, c, d, x[i+ 4], 7 , -176418897)  d = ff(d, a, b, c, x[i+ 5], 12, 1200080426)  c = ff(c, d, a, b, x[i+ 6], 17, -1473231341)  b = ff(b, c, d, a, x[i+ 7], 22, -45705983)  a = ff(a, b, c, d, x[i+ 8], 7 , 1770035416)  d = ff(d, a, b, c, x[i+ 9], 12, -1958414417)  c = ff(c, d, a, b, x[i+10], 17, -42063)  b = ff(b, c, d, a, x[i+11], 22, -1990404162)  a = ff(a, b, c, d, x[i+12], 7 , 1804603682)  d = ff(d, a, b, c, x[i+13], 12, -40341101)  c = ff(c, d, a, b, x[i+14], 17, -1502002290)  b = ff(b, c, d, a, x[i+15], 22, 1236535329)  a = gg(a, b, c, d, x[i+ 1], 5 , -165796510)  d = gg(d, a, b, c, x[i+ 6], 9 , -1069501632)  c = gg(c, d, a, b, x[i+11], 14, 643717713)  b = gg(b, c, d, a, x[i+ 0], 20, -373897302)  a = gg(a, b, c, d, x[i+ 5], 5 , -701558691)  d = gg(d, a, b, c, x[i+10], 9 , 38016083)  c = gg(c, d, a, b, x[i+15], 14, -660478335)  b = gg(b, c, d, a, x[i+ 4], 20, -405537848)  a = gg(a, b, c, d, x[i+ 9], 5 , 568446438)  d = gg(d, a, b, c, x[i+14], 9 , -1019803690)  c = gg(c, d, a, b, x[i+ 3], 14, -187363961)  b = gg(b, c, d, a, x[i+ 8], 20, 1163531501)  a = gg(a, b, c, d, x[i+13], 5 , -1444681467)  d = gg(d, a, b, c, x[i+ 2], 9 , -51403784)  c = gg(c, d, a, b, x[i+ 7], 14, 1735328473)  b = gg(b, c, d, a, x[i+12], 20, -1926607734)  a = hh(a, b, c, d, x[i+ 5], 4 , -378558)  d = hh(d, a, b, c, x[i+ 8], 11, -2022574463)  c = hh(c, d, a, b, x[i+11], 16, 1839030562)  b = hh(b, c, d, a, x[i+14], 23, -35309556)  a = hh(a, b, c, d, x[i+ 1], 4 , -1530992060)  d = hh(d, a, b, c, x[i+ 4], 11, 1272893353)  c = hh(c, d, a, b, x[i+ 7], 16, -155497632)  b = hh(b, c, d, a, x[i+10], 23, -1094730640)  a = hh(a, b, c, d, x[i+13], 4 , 681279174)  d = hh(d, a, b, c, x[i+ 0], 11, -358537222)  c = hh(c, d, a, b, x[i+ 3], 16, -722521979)  b = hh(b, c, d, a, x[i+ 6], 23, 76029189)  a = hh(a, b, c, d, x[i+ 9], 4 , -640364487)  d = hh(d, a, b, c, x[i+12], 11, -421815835)  c = hh(c, d, a, b, x[i+15], 16, 530742520)  b = hh(b, c, d, a, x[i+ 2], 23, -995338651)  a = ii(a, b, c, d, x[i+ 0], 6 , -198630844)  d = ii(d, a, b, c, x[i+ 7], 10, 1126891415)  c = ii(c, d, a, b, x[i+14], 15, -1416354905)  b = ii(b, c, d, a, x[i+ 5], 21, -57434055)  a = ii(a, b, c, d, x[i+12], 6 , 1700485571)  d = ii(d, a, b, c, x[i+ 3], 10, -1894986606)  c = ii(c, d, a, b, x[i+10], 15, -1051523)  b = ii(b, c, d, a, x[i+ 1], 21, -2054922799)  a = ii(a, b, c, d, x[i+ 8], 6 , 1873313359)  d = ii(d, a, b, c, x[i+15], 10, -30611744)  c = ii(c, d, a, b, x[i+ 6], 15, -1560198380)  b = ii(b, c, d, a, x[i+13], 21, 1309151649)  a = ii(a, b, c, d, x[i+ 4], 6 , -145523070)  d = ii(d, a, b, c, x[i+11], 10, -1120210379)  c = ii(c, d, a, b, x[i+ 2], 15, 718787259)  b = ii(b, c, d, a, x[i+ 9], 21, -343485551)  a = safe_add(a, olda)  b = safe_add(b, oldb)  c = safe_add(c, oldc)  d = safe_add(d, oldd)  }  return [a, b, c, d]
}  /* * Convert an array of little-endian words to a hex string. */
function binl2hex(binarray)
{  var hex_tab = "0123456789abcdef"  var str = ""  for(var i = 0; i < binarray.length * 4; i++)  {  str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +  hex_tab.charAt((binarray[i>>2] >> ((i%4)*8)) & 0xF)  }  return str
}  /* * Convert an array of little-endian words to a base64 encoded string. */
function binl2b64(binarray)
{  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"  var str = ""  for(var i = 0; i < binarray.length * 32; i += 6)  {  str += tab.charAt(((binarray[i>>5] << (i%32)) & 0x3F) |  ((binarray[i>>5+1] >> (32-i%32)) & 0x3F))  }  return str
}  /* * Convert an 8-bit character string to a sequence of 16-word blocks, stored * as an array, and append appropriate padding for MD4/5 calculation. * If any of the characters are >255, the high byte is silently ignored. */
function str2binl(str)
{  var nblk = ((str.length + 8) >> 6) + 1 // number of 16-word blocks  var blks = new Array(nblk * 16)  for(var i = 0; i < nblk * 16; i++) blks[i] = 0  for(var i = 0; i < str.length; i++)  blks[i>>2] |= (str.charCodeAt(i) & 0xFF) << ((i%4) * 8)  blks[i>>2] |= 0x80 << ((i%4) * 8)  blks[nblk*16-2] = str.length * 8  return blks
}  /* * Convert a wide-character string to a sequence of 16-word blocks, stored as * an array, and append appropriate padding for MD4/5 calculation. */
function strw2binl(str)
{  var nblk = ((str.length + 4) >> 5) + 1 // number of 16-word blocks  var blks = new Array(nblk * 16)  for(var i = 0; i < nblk * 16; i++) blks[i] = 0  for(var i = 0; i < str.length; i++)  blks[i>>1] |= str.charCodeAt(i) << ((i%2) * 16)  blks[i>>1] |= 0x80 << ((i%2) * 16)  blks[nblk*16-2] = str.length * 16  return blks
}  /* * External interface */
function hexMD5 (str) { return binl2hex(coreMD5( str2binl(str))) }
function hexMD5w(str) { return binl2hex(coreMD5(strw2binl(str))) }
function b64MD5 (str) { return binl2b64(coreMD5( str2binl(str))) }
function b64MD5w(str) { return binl2b64(coreMD5(strw2binl(str))) }
/* Backward compatibility */
function calcMD5(str) { return binl2hex(coreMD5( str2binl(str))) }
module.exports = {  hexMD5: hexMD5
}  

3.index.js(重点重点重点)

const app = getApp()
import {hexMD5
} from "../../utils/md5.js"
// 提示音
let innerAudioContext = wx.createInnerAudioContext()
innerAudioContext.src = '/img/scanvoice.mp3'
let animation = wx.createAnimation({});
let donghuaInterVal;// 创建img标签
function createImage(canvas, url) {return new Promise((resolve, reject) => {const img = canvas.createImage();img.src = url;img.onload = res => {resolve(img);};img.onerror = err => {console.log('create image error', err, url)reject(err);};});
}function querySelector(elementId, page) {const query = wx.createSelectorQuery();return new Promise(resove => {query.in(page).select(/^#/.test(elementId) ? elementId : '#' + elementId).fields({node: true,size: true,rect: true}).exec(res => {resove(res);});});
}Page({data: {indicatorDots: true,autoplay: false,interval: 2000,duration: 500,isShowCamera: false,isShowScan: false,// 相机实例ctx: '',// 帧数据实例listener: '',// 锁lock: false,// 每n秒请求间隔fpsTime: 500,isHidden: true,num: 0,imgShow: false,isShow: true,imgList: [],name: '',tempImgUrl: '',i: 0,imgNum: '000',textShow: false,show: true,getframe: false,width: 1334,height: 750,},t: Date.now(),s: 0,changeIndicatorDots() {this.setData({indicatorDots: !this.data.indicatorDots})},changeAutoplay() {this.setData({autoplay: !this.data.autoplay})},intervalChange(e) {this.setData({interval: e.detail.value})},durationChange(e) {this.setData({duration: e.detail.value})},// 再玩一次toIndex() {wx.redirectTo({url: '/pages/index/index',})},// 分享onShareAppMessage(options) {return {title: '参与就有机会免费领取观影券',imageUrl: '/img/share.png',path: '/pages/index/index',success: function (res) {console.log(res, '成功')console.info(res + '成功');wx.showToast({title: '分享成功',})// 转发成功},fail: function (res) {console.log(res + '失败');// 转发失败},complete: function (res) {// 不管成功失败都会执行console.log(res, '成功或失败')wx.showToast({title: '成功或失败',})}}},onLoad() {// console.log('进入小程序');this.donghua()// 监听小程序内存wx.onMemoryWarning(function () {console.log('内存不足')})},// 修复方法:unload 中进行清理onUnload() {console.log('小程序销毁');this.fnStop()// clearInterval(this.data.timer)clearInterval(donghuaInterVal)},// 权限open() {wx.authorize({scope: 'scope.camera'}).then(res => {this.setData({isShowCamera: true})}).catch(() => {wx.getSetting().then(res => {if (!res.authSetting['scope.camera']) {wx.showModal({title: '是否授权摄像头',content: '请确认授权,否则无法正常使用',success: (tip) => {if (tip.confirm)wx.openSetting()}})}})})},// 1.相机初始化bindinitdone() {this.data.ctx = wx.createCameraContext()this.data.listener = this.data.ctx.onCameraFrame(frame => {if (!this.data.lock) {this.data.lock = truethis.scan(frame)}})this.data.listener.start()this.setData({isShowScan: true})},// 2.相机实时帧数据转成base64,调取第三方图像识别算法scan(frame) {let that = this// console.log('222');console.log(frame)// 将像素数据绘制到画布let data = new Uint8ClampedArray(frame.data)let clamped = new Uint8ClampedArray(data)wx.canvasPutImageData({canvasId: 'myCanvas',x: 0,y: 0,width: frame.width,height: frame.height,data: clamped,success(res) {// 转换临时文件setTimeout(() => {wx.canvasToTempFilePath({x: 0,y: 0,width: frame.width,height: frame.height,canvasId: 'myCanvas',fileType: 'jpg',destWidth: frame.width,destHeight: frame.height,// 精度修改quality: 0.5,success(res) {// 临时文件转base64wx.getFileSystemManager().readFile({filePath: res.tempFilePath, //选择图片返回的相对路径encoding: 'base64', //编码格式success: res => {// 保存base64// console.log(res.data);let base64 = 'data:image/jpeg;base64,' + res.data// console.log(base64);that.fnCloudDetect(base64)}})},fail(res) {console.log(res);}}, that)setTimeout(() => {that.data.lock = false}, that.data.fpsTime)}, 600)},fail(err) {console.log('图像数据到canvas失败', err)}})// }},// 3.第三方图像识别算法接口fnCloudDetect(base64) {let that = thisconst appId = '100000000628';const timestamp = String(Math.floor(Date.now() / 1000));const nonce = 'weopripowis';const requestId = 'cvjkxcvjxcvnm';const version = 'v2';const appKey = '954cnKRvf9P57JmL';const requestUrl = 'https://gw-ezxr.netease.com/pigeon-image/api/alg/pigeon/image/recog';const md5_string = "appId=" + appId + "&imageEncodingData=" + encodeURIComponent(base64) + "&nonce=" + encodeURIComponent(nonce) +"&requestId=" + encodeURIComponent(requestId) + "&timestamp=" + encodeURIComponent(timestamp) + "&appkey=" + appKey;const sign = hexMD5(md5_string).toString().toUpperCase();return new Promise((resolve, reject) => {wx.request({url: requestUrl,method: "POST",header: {appId,nonce,timestamp,sign,version,},data: {appId,requestId,imageEncodingData: base64,},success: (result) => {// console.log("success", result);if (result.data.detail.status === 1 && result.data.status === '000000') {resolve(result.data)if (!that.data.getframe) {that.setData({name: result.data.detail.returnData,show: false,getframe: true})that.initImgList(result.data.detail.returnData)that.data.listener.stop();clearInterval(donghuaInterVal)innerAudioContext.play()wx.showLoading({title: '精彩即将呈现'})console.log('停止获取实时帧')}}reject(result.data.desc);},fail: (err) => {// console.log("fail", err);reject("request faild");}})})},// 4.图片列表initImgList(name) {var tempList = [],baseurl, countNum = 0;if (name == '伯爵') {// baseurl = 'http://bronet.qiniu.bronet.cn/Dracula/Dracula_00' // 自己的baseurl = 'https://hpkxjar.una-ad.com/public/Dracula/Dracula_00' // 客户的countNum = 144} else if (name == '女婿') {// baseurl = 'http://bronet.qiniu.bronet.cn/Johnny/johnny_00'baseurl = 'https://hpkxjar.una-ad.com/public/Johnny/johnny_00'countNum = 103} else {// baseurl = 'http://bronet.qiniu.bronet.cn/Blobby/Blobby_00'baseurl = 'https://hpkxjar.una-ad.com/public/Blobby/Blobby_00'countNum = 139}for (let i = 0; i < countNum; i++) {let l = JSON.stringify(i).lengthlet baseIndex = l == 1 ? ('00' + i) : l == 2 ? ('0' + i) : i;let tempUrl = baseurl + baseIndex + '.png'tempList.push(tempUrl)}this.initCanvas(tempList);},// 5.创建画布async initCanvas(imgs) {const canvasQuery = await querySelector('#oneTitleFrameCanvas', this);const cnv = canvasQuery[0].node;const ctx = cnv.getContext('2d');const dpr = wx.getSystemInfoSync().pixelRatio;cnv.width = cnv.width * dpr;cnv.height = cnv.height * dpr;this.cnv = cnv;this.ctx = ctx;const imgNodes = await Promise.all(imgs.map(v => createImage(cnv, v)));this.imgNodes = imgNodes;this.fnPlay();},// 6.序列帧动画播放fnPlay() {// console.log('开始播放');wx.hideLoading()this.tickCount = this.cnv.requestAnimationFrame(() => {if (Date.now() - this.t >= 1000 / 16) {this.t = Date.now();if (this.s > this.imgNodes.length) {this.s = 0;this.fnPlay();this.setData({textShow: true})} else {this.ctx.clearRect(0, 0, this.cnv.width, this.cnv.height);if (this.imgNodes[this.s])this.ctx.drawImage(this.imgNodes[this.s],0,0,this.cnv.width,this.cnv.height);this.fnPlay();this.setData({textShow: true})this.s++;}} else {this.fnPlay();}});},// 释放序列帧fnStop() {console.log('停止播放序列帧');this.cnv.cancelAnimationFrame(this.tickCount);},// 禁止第二屏滑动stopTouchMove: function () {return true;},// 扫描动画donghua() {var that = this;// 控制向上还是向下移动let m = truedonghuaInterVal = setInterval(function () {setTimeout(() => {if (m) {animation.translateY(500).step({duration: 2000})m = !m;} else {animation.translateY(-500).step({duration: 2000})m = !m;}that.setData({animation: animation.export()})}, 1)}.bind(this), 3000)},// 上滑动画swiperChange(e) {this.fnStop()if (e.detail.current == 1) {wx.redirectTo({url: '/pages/video/video',})}}
})

做一个AR识别IP人物形象的微信小程序相关推荐

  1. 如何在完全不懂服务器开发的情况下做一个实时联网对战的微信小游戏

    微信小游戏即将开放?有我们在,你还赶得上! 根据微信官方对外公开的消息,微信小游戏的脚步越来越接近了.它的开发者资格门槛和使用者门槛都很低,以后必将引爆一波"全民开发小游戏"浪潮. ...

  2. 利用css transition属性实现一个带动画显隐的微信小程序部件

    我们先来看效果图 像这样的一个带过渡效果的小部件在我们实际开发中的应用几率还是比较大的,但是在开发微信小程序的过程中可能有的小伙伴发现transition这个属性它不好使(下面说明)所以我们这个时候会 ...

  3. ui测试怎么做?依据文档有哪些_微信小程序开发流程有哪些?各个环节注意事项...

    微信小程序开发流程有哪些?各个环节注意事项是什么?微信小程序开发有哪些注意事项,开发流程是怎样的?下面就一起来看看吧. 1.微信小程序开发之注册小程序 在开发小程序之前,首先就是要注册一个小程序.不过 ...

  4. 耗时一个月上架了一款微信小程序,赚了2022年的第一笔副收入

    今天不谈技术,只谈经历. 前戏 相信有很多的程序员都有一个产品梦,希望有一款属于自己产品.毕竟工作中遇到的有些"脑残"的产品经理不是一个两个,最后不得不因为"技术服务于业 ...

  5. h5+js调取相机做取景框_使用Vue.js开发微信小程序:开源框架mpvue解析

    戳蓝字"CSDN云计算"关注我们哦! 作者 | 成全 责编 | 阿秃 转自 | 美团技术团队企业博客 前言 mpvue是一款使用Vue.js开发微信小程序的前端框架.使用此框架,开 ...

  6. 2019小程序没必要做了_企业有必要开发微信小程序吗?

    互联网的不断创新发展,微信这一社交软件技术逐渐成为了社会大众信息沟通工具之一,但是随着小程序的出现,让更多的企业发现微信开发能帮助企业挖掘市场,获取精准流量的手段,于是,不得不开始工作,那么,企业开发 ...

  7. 动手开发一个名为“微天气”的微信小程序(下)

    摘要: 上期我们介绍了"微天气"的API与界面代码编写,今天我们继续介绍逻辑层代码以及查询代码的编写. 本文选自<从零开始学微信小程序开发>. 编写逻辑层代码 由于在i ...

  8. 微信可以识别哪些HTML语言,微信小程序一些常用标签与HTML的对应关系

    参考地址: https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/ 微信小程序中的常用标签: view(视图容器). ...

  9. 互动小剧本 | 我做了一款类似Hooked的微信小程序

    作为一名开发者,闲来无事都是逛逛论坛,逛逛GitHub.在无意中发现了这个项目.跟我以前想要的对话式小说很相似,php写的阅读器,想要的编辑器没有,于是就萌生了写个微信的对话式小程序. 再细看作者原来 ...

最新文章

  1. uc/os内存管理的理解
  2. 嘉宾及议程速览,第四范式2021发布会进入一周倒计时
  3. Auto.js Pro如何连接VS Code插件
  4. 洛谷 P1202 模拟 - 黑色星期五 Friday the Thirteenth
  5. XtraBackup全备与增量备份
  6. 原创:MD5 32位加密软件
  7. java 浏览器 爬虫_java 网络编程-爬虫+模拟浏览器
  8. DNS协议及客户端实现
  9. Leetcode每日一题:1122.relative-sort-array(数组的相对排序)
  10. springBoot上传文件时MultipartFile报空问题解决方法
  11. bzoj 1413: [ZJOI2009]取石子游戏(博弈+DP)
  12. scrapy分布式调度源码及其实现过程
  13. 最新Java面试资料整理
  14. Java Web之三大利器
  15. Arduino 交通灯实验
  16. 网页转化成pdf,网页转换图片,wkhtmltopdf,wkhtmltoimage使用小结
  17. 用Python绘制一个简单的奥运五环
  18. 多个containers 共用一个pvc_庆阳PVC排水管生产线销售,真不错_崔氏机械
  19. 铁打的阿里,流水的美团,21届校招生Offer薪资曝光后,伤了老员工的心…
  20. ps4移植android游戏,安卓玩PS4游戏 remote play安装教程

热门文章

  1. 后缀自动机 (WJMZBMR讲稿的整理和注释)
  2. 甲骨文中国裁员,N+6赔偿……部分员工抗议
  3. 解决win7无法默认选择的Everdit程序打开文件
  4. 搜索插件实现热搜词,历史查询功能
  5. Linux部署JupyterHub实现多用户使用Jupyterlab
  6. 自考计算机网络原理笔记,自考计算机网络原理笔记(典藏版).pdf
  7. Linux以及银河麒麟系统的一些基本认识
  8. 移动端canvas画板绘图工具
  9. 北航考研软件学院电子信息991专业课备考
  10. oracle 表导出pdm文件,如何将数据库中已有表导入到powerDesigner生成pdm文件