Created
May 2, 2021 07:52
-
-
Save ChenYFan/da9c02ca543116f851c30db87957a99a to your computer and use it in GitHub Desktop.
hostloc论坛头像上传非flash修正版本
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Discuz论坛头像上传助手 | |
// @author 枫谷剑仙 | |
// @description 突破图片尺寸、GIF帧数限制,无损上传 | |
// @version 2.0.6 | |
// @namespace http://www.mapaler.com/ | |
// @include */home.php?mod=spacecp&ac=avatar* | |
// @icon https://gitee.com/ComsenzDiscuz/DiscuzX/raw/master/upload/uc_server/images/noavatar_small.gif | |
// @grant unsafeWindow | |
// @grant GM_xmlhttpRequest | |
// ==/UserScript== | |
(function(){ | |
'use strict'; | |
let embed = document.getElementsByTagName("embed")[0]; | |
embed.src = embed.src.replace(/www\.hostloc\.com/g, "hostloc.com"); | |
const avatarform = document.querySelector("#avatarform") || | |
document.querySelector("form[action^=home]"); //以前没有HTML5的老版本,没有#avatarform | |
if (!avatarform) return; | |
let noGM_xmlhttpRequest = false; | |
//仿GM_xmlhttpRequest函数v1.4 | |
if (typeof(GM_xmlhttpRequest) == 'undefined' || typeof(GM_info) == 'undefined') | |
{ | |
noGM_xmlhttpRequest = true; | |
window.GM_xmlhttpRequest = function(GM_param) { | |
const xhr = new XMLHttpRequest(); //创建XMLHttpRequest对象 | |
xhr.open(GM_param.method, GM_param.url, true); | |
if (GM_param.responseType) xhr.responseType = GM_param.responseType; | |
if (GM_param.overrideMimeType) xhr.overrideMimeType(GM_param.overrideMimeType); | |
xhr.onreadystatechange = function(e) //设置回调函数 | |
{ | |
const _xhr = e.target; | |
if (_xhr.readyState === _xhr.DONE) { //请求完成时 | |
if (_xhr.status === 200 && GM_param.onload) //正确加载时 | |
{ | |
GM_param.onload(_xhr); | |
} | |
if (_xhr.status !== 200 && GM_param.onerror) //发生错误时 | |
{ | |
GM_param.onerror(_xhr); | |
} | |
} | |
}; | |
if (GM_param.onprogress) | |
xhr.upload.onprogress = function(e){GM_param.onprogress(e.target)}; | |
//添加header | |
for (let header in GM_param.headers) { | |
xhr.setRequestHeader(header, GM_param.headers[header]); | |
} | |
//发送数据 | |
xhr.send(GM_param.data ? GM_param.data : null); | |
}; | |
} | |
const avatarsDefine = [ | |
{name:'大头像',code:'big',maxWidth:200,maxHeight:250,blob:null}, | |
{name:'中头像',code:'middle',maxWidth:120,maxHeight:120,blob:null}, | |
{name:'小头像',code:'small',maxWidth:48,maxHeight:48,blob:null}, | |
]; | |
const html5mode = Boolean(avatarform.querySelector('#avatardesigner')); //HTML5模式还是Flash | |
const insertPlace = avatarform.parentNode; | |
// HTML5版本才会有的几个提交按钮 | |
const ipt_avatarArr = [ | |
avatarform.querySelector('[name="avatar1"]'), | |
avatarform.querySelector('[name="avatar2"]'), | |
avatarform.querySelector('[name="avatar3"]'), | |
]; | |
const ipt_Filedata = avatarform.querySelector('[name="Filedata"]'); | |
const ipt_confirm = avatarform.querySelector('[name="confirm"]'); | |
let data = (typeof(unsafeWindow) == 'undefined' ? window : unsafeWindow).data; | |
// Flash版本的Flash | |
const swf_mycamera = avatarform.querySelector('[name="mycamera"]'); | |
if (!html5mode && !swf_mycamera) | |
{ //解决垃圾机锋论坛的问题 | |
const table1 = avatarform.querySelector('table'); | |
const t1cell = table1.tBodies[0].rows[0].cells[0]; | |
const avatarSrc = t1cell.querySelector('img').src; | |
const fiexdApiUrl = avatarSrc.substring(0,avatarSrc.indexOf('/data/avatar')); | |
const table2 = avatarform.querySelector('table:nth-of-type(2)'); | |
const t2cell = table2.tBodies[0].rows[0].cells[0]; | |
const scriptHTML = t2cell.querySelector('script').innerHTML; | |
const regRes = /document\.write\(AC_FL_RunContent\((.+?)\)\);/i.exec(scriptHTML); | |
data = regRes[1].split(',').map(str=>str.replace(/^'|'$/g,'')); | |
const brokenSwfUrl = data[data.indexOf('src')+1]; | |
const swfUrlParse = new URL(fiexdApiUrl + brokenSwfUrl.substr(brokenSwfUrl.indexOf('/images/camera.swf'))); | |
swfUrlParse.searchParams.set('ucapi',fiexdApiUrl); | |
data[data.indexOf('src')+1] = swfUrlParse.toString(); | |
} | |
const swfUrl = new URL(data ? data[data.indexOf('src')+1] : swf_mycamera.src); | |
const maxSize = parseInt(swfUrl.searchParams.get('uploadSize') || 2048, 10) * 1024; | |
const styleCss = `.discuz-avatar{ | |
border: 1px solid #ccc; | |
padding: 5px 15px; | |
width:auto; | |
display:inline-block; | |
width: 450px; | |
box-sizing: border-box; | |
} | |
.discuz-avatar h3{ | |
text-align:center; | |
} | |
.pic-type-div{ | |
display:inline-block; | |
vertical-align:top; | |
margin-right: 15px; | |
} | |
.pic-type-div:last-of-type{ | |
margin-right: unset; | |
} | |
.pic-div{ | |
border: 1px solid #ccc; | |
cursor: pointer; | |
position: relative; | |
display: table-cell; | |
text-align:center; | |
vertical-align: middle; | |
background: #fff; | |
background-image: | |
linear-gradient(45deg, #eee 25%, transparent 26%, transparent 74%, #eee 75%), | |
linear-gradient(45deg, #eee 25%, transparent 26%, transparent 74%, #eee 75%); | |
background-position: 0 0, 10px 10px; | |
background-size: 20px 20px; | |
} | |
.pic-type-big .pic-div{ | |
width: 200px; | |
height: 250px; | |
} | |
.pic-type-big .pic-img{ | |
max-width: 200px; | |
max-height: 250px; | |
} | |
.pic-type-middle .pic-div{ | |
width: 120px; | |
height: 120px; | |
} | |
.pic-type-middle .pic-img{ | |
max-width: 120px; | |
max-height: 120px; | |
} | |
.pic-type-small .pic-div{ | |
width: 48px; | |
height: 48px; | |
} | |
.pic-type-small .pic-img{ | |
max-width: 48px; | |
max-height: 48px; | |
} | |
.choose-file{ | |
display: none; | |
} | |
.pic-div.nopic::before{ | |
content:"➕"; | |
font-size: 2em; | |
} | |
.pic-tag{ | |
text-align:center; | |
} | |
.submit-bar{ | |
text-align:center; | |
} | |
/*Flash AJAX状态使用*/ | |
.status-bar{ | |
font-size:2em; | |
background-repeat: no-repeat; | |
background-position: center; | |
margin:0px auto; | |
display:none; | |
text-align: center; | |
} | |
.status-bar[data-status]{ | |
display:block; | |
} | |
@keyframes loading-animate{ | |
from { | |
transform: rotate(0deg); | |
} | |
to { | |
transform: rotate(3600deg); | |
} | |
} | |
.status-bar[data-status="loading"]::before { | |
display: inline-block; | |
border: 4px SteelBlue dotted; | |
border-radius: 50%; | |
content:""; | |
width: 1em; | |
height: 1em; | |
animation: loading-animate 50s infinite linear; | |
} | |
.status-bar[data-status="success"]::before { | |
content:"✔️"; | |
} | |
.status-bar[data-status="error"]::before { | |
content:"❌"; | |
} | |
.progress-bar{ | |
padding: 5px; | |
text-align: center; | |
}`; | |
const fragment = document.createDocumentFragment(); | |
const ctlDiv = fragment.appendChild(document.createElement('div')); | |
ctlDiv.className = 'discuz-avatar'; | |
const style = ctlDiv.appendChild(document.createElement('style')); | |
style.type = 'text/css'; | |
style.innerHTML = styleCss; | |
const caption = ctlDiv.appendChild(document.createElement('h3')); | |
caption.appendChild(document.createTextNode(typeof(GM_info) != 'undefined' ?`${GM_info.script.name} ${GM_info.script.version}`:'无脚本扩展,直接执行脚本')); | |
caption.appendChild(document.createElement('br')); | |
caption.appendChild(document.createTextNode(`${html5mode?'HTML5':'Flash'}模式`)); | |
const picTable = ctlDiv.appendChild(document.createElement('div')); | |
const picImgs = []; | |
avatarsDefine.forEach((obj,idx)=>{ | |
const picTypeDiv = picTable.appendChild(document.createElement('div')); | |
picTypeDiv.className = 'pic-type-div pic-type-' + obj.code; | |
const picDiv = picTypeDiv.appendChild(document.createElement('div')); | |
picDiv.className = 'pic-div nopic'; | |
const pic = new Image(); | |
picDiv.appendChild(pic); | |
pic.className = 'pic-img img-' + obj.code; | |
pic.onload = function(){ | |
if (this.naturalWidth > obj.maxWidth) | |
{ | |
progressDiv.appendChild(document.createElement('br')); | |
progressDiv.appendChild(document.createTextNode(`${obj.name}宽度大于 ${obj.maxWidth}px,可能可能上传失败!`)); | |
} | |
if (this.naturalHeight > obj.maxHeight) | |
{ | |
progressDiv.appendChild(document.createElement('br')); | |
progressDiv.appendChild(document.createTextNode(`${obj.name}高度大于 ${obj.maxHeight}px,可能可能上传失败!`)); | |
} | |
} | |
picImgs.push(pic); | |
const file = picDiv.appendChild(document.createElement('input')); | |
file.type = "file"; | |
file.className = "choose-file"; | |
picDiv.onclick = function(){ | |
file.click(); | |
} | |
file.onchange = function(e){ | |
const file = e.target.files[0]; | |
const imageType = /image\/.*/i; | |
progressDiv.textContent = ''; | |
if (!imageType.test(file.type)) { | |
progressDiv.textContent = `${file.name} 不是有效的图像文件!`; | |
pic.src = ''; | |
picDiv.classList.add('nopic'); | |
return; | |
} | |
if (file.size > maxSize) { | |
progressDiv.textContent = `${obj.name} ${file.name} 文件大小超出 ${maxSize/1048576}MiB,可能上传失败!`; | |
} | |
picDiv.classList.remove('nopic'); | |
if (pic.src.length>0) | |
URL.revokeObjectURL(pic.src); | |
pic.src = URL.createObjectURL(file); | |
obj.blob = file; | |
} | |
const tagDiv = picTypeDiv.appendChild(document.createElement('div')); | |
tagDiv.className = 'pic-tag'; | |
const span1 = tagDiv.appendChild(document.createElement('span')); | |
span1.appendChild(document.createTextNode(obj.name)); | |
tagDiv.appendChild(document.createElement('br')); | |
const span2 = tagDiv.appendChild(document.createElement('span')); | |
span2.appendChild(document.createTextNode(`${obj.maxWidth}×${obj.maxHeight}`)); | |
}); | |
const statusDiv = ctlDiv.appendChild(document.createElement('div')); | |
statusDiv.className = 'status-bar'; | |
const progressDiv = ctlDiv.appendChild(document.createElement('div')); | |
progressDiv.className = 'progress-bar'; | |
const submitDiv = ctlDiv.appendChild(document.createElement('div')); | |
submitDiv.className = 'submit-bar'; | |
const submit = submitDiv.appendChild(document.createElement('button')); | |
submit.className = 'submit-btn'; | |
submit.innerHTML = '📤提交'; | |
submit.onclick = function(){ | |
if (!avatarsDefine.every(obj=>obj.blob)) | |
{ | |
progressDiv.textContent = `还未添加 ${avatarsDefine.filter(obj=>!obj.blob).map(obj=>obj.name).join('、')} 图像`; | |
return; | |
} | |
submit.disabled = true; | |
const fileDataArr = []; | |
function readBlobs(blobArr,type,callback) | |
{ | |
if (blobArr.length<1) | |
{ | |
callback(fileDataArr); | |
return; | |
} | |
const file = blobArr.shift(); | |
const fileReader = new FileReader(); | |
fileReader.onload = function (e) { | |
fileDataArr.push(e.target.result); | |
readBlobs(blobArr, type, callback); | |
} | |
if (type == 'base64') | |
fileReader.readAsDataURL(file); | |
else //if (type == 'arrayBuffer') | |
fileReader.readAsArrayBuffer(file); | |
} | |
readBlobs(avatarsDefine.map(obj=>obj.blob), html5mode ? 'base64':'arrayBuffer', (html5mode ? sumbitAvatarsHTML5 : sumbitAvatarsFlash)); | |
} | |
ctlDiv.appendChild(document.createElement('hr')); | |
const tipsDiv = ctlDiv.appendChild(document.createElement('div')); | |
tipsDiv.className = 'tips-bar'; | |
let quote = null,code = null; | |
if (!html5mode) | |
{ | |
console.log(new URL(_parseBasePath(swfUrl)).host,location.host,noGM_xmlhttpRequest) | |
if (noGM_xmlhttpRequest && new URL(_parseBasePath(swfUrl)).host != location.host) | |
{ | |
quote = submitDiv.appendChild(document.createElement('div')); | |
quote.className = 'quote'; | |
quote.appendChild(document.createTextNode('该站点 UCenter 跨域,目前为直接执行模式无法处理 Flash 跨域问题。请使用脚本扩展,或使用 DZX3.4 的 HTML5 模式。')); | |
} | |
quote = tipsDiv.appendChild(document.createElement('div')); | |
quote.className = 'quote'; | |
quote.appendChild(document.createTextNode('若上传100%后显示')); | |
code = quote.appendChild(document.createElement('div')); | |
code.className = 'blockcode'; | |
code.appendChild(document.createTextNode('<?xml version="1.0" ?><root><face success="0"/></root>')); | |
quote.appendChild(document.createTextNode('可能是图像像素超出服务器后台限制,或格式不被 PHP 支持。')); | |
quote = tipsDiv.appendChild(document.createElement('div')); | |
quote.className = 'quote'; | |
quote.appendChild(document.createTextNode('若上传显示')); | |
code = quote.appendChild(document.createElement('div')); | |
code.className = 'blockcode'; | |
code.appendChild(document.createTextNode('Access denied for agent changed')); | |
quote.appendChild(document.createTextNode('可能是你的活动状态失效了需要刷新,或者是 Discuz 和 UCenter 通信没配好,请直接联系网站管理员。')); | |
} | |
quote = tipsDiv.appendChild(document.createElement('div')); | |
quote.className = 'quote'; | |
quote.appendChild(document.createTextNode('PHP 7.1 才支持 WebP 格式,若 WebP 上传失败可能是服务器后端检查时失败。想上传动画还是乖乖用 APNG 或 GIF。')); | |
//将UI插入 | |
insertPlace.appendChild(fragment); | |
//HTML5模式提交 | |
function sumbitAvatarsHTML5(base64Arr) | |
{ | |
progressDiv.textContent = '已提交,HTML5 模式成功状态请直接参考上方编辑器'; | |
const dataArr = base64Arr.map(str=>str.substr(str.indexOf(",") + 1)); //拿到3个头像的Base64字符串 | |
dataArr.forEach((str,idx)=>{ | |
ipt_avatarArr[idx].value = str; | |
}); | |
ipt_Filedata.value = ''; | |
ipt_confirm.value = ''; | |
avatarform.action = swfUrl.toString().replace('images/camera.swf?inajax=1', 'index.php?m=user&a=rectavatar&base64=yes').replace('www.hostloc.com', 'hostloc.com'); //来自官方代码: static/avatar/avatar.js?EMK,你敢信?官方代码居然就是字符串替换 | |
avatarform.target='rectframe'; | |
avatarform.submit(); | |
submit.disabled = false; | |
} | |
//Flash模式提交 | |
function sumbitAvatarsFlash(arrayBufferArr) | |
{ | |
statusDiv.setAttribute('data-status','loading'); | |
const dataArr = arrayBufferArr.map(bytes=>{ | |
const uint8Array = new Uint8Array(bytes); | |
const numArray = Array.from(uint8Array); | |
const strArray = numArray.map(bit=>`${bit<16?0:''}${bit.toString(16)}`); | |
return strArray.join('').toUpperCase(); | |
}); | |
const sp = swfUrl.searchParams; | |
const loc1 = _parseBasePath(swfUrl); | |
const apiUrl = new URL(`${loc1}index.php`); | |
apiUrl.protocol = location.protocol; //解决http和https混合内容的问题 | |
const asp = apiUrl.searchParams; | |
asp.set('m','user'); | |
asp.set('inajax',1); | |
asp.set('a','rectavatar'); | |
asp.set('appid',sp.get('appid')); | |
asp.set('input',sp.get('input')); | |
asp.set('agent',sp.get('agent')); | |
asp.set('avatartype',sp.get('avatartype')); | |
const post = new URLSearchParams(); | |
dataArr.forEach((str,idx)=>{ | |
post.set(`avatar${idx+1}`,str) | |
}); | |
post.set('urlReaderTS',Date.now()); | |
GM_xmlhttpRequest({ | |
method: "POST", | |
url: apiUrl, | |
data: post.toString(), | |
headers: {"Content-Type": "application/x-www-form-urlencoded"}, | |
onload: onloadHandler, | |
onerror: onerrorHandler, | |
onprogress: uploadOnprogressHandler | |
}); | |
} | |
//Flash模式的传统方法 | |
function _parseBasePath(arg1) | |
{ | |
let loc1 = arg1.searchParams.get('ucapi'); | |
if (loc1.length > 0 && !(loc1.substring((loc1.length - 1)) == "/")) | |
{ | |
loc1 = loc1 + "/"; | |
} | |
if (loc1.length > 0 && !new RegExp("^https?://", "i").test(loc1)) | |
{ | |
loc1 = "http://" + loc1; | |
} | |
return loc1; | |
} | |
function onloadHandler(response) { | |
progressDiv.textContent = "100%"; | |
const xml = response.responseXML; | |
console.log(xml) | |
if (xml) { | |
const success = xml.querySelector('face'); | |
if (success != null && success.getAttribute("success") == 1) { | |
statusDiv.setAttribute('data-status','success'); | |
} else { | |
statusDiv.setAttribute('data-status','error'); | |
const message = xml.querySelector('message'); | |
if (message) | |
progressDiv.textContent = message.getAttribute('type') + ': ' + message.getAttribute('value'); | |
else | |
progressDiv.textContent = response.responseText; | |
} | |
} else { | |
statusDiv.setAttribute('data-status','error'); | |
progressDiv.textContent = 'error: no responseXML'; | |
} | |
onloadendHandler(); | |
} | |
function onerrorHandler(e) { | |
statusDiv.setAttribute('data-status','error'); | |
onloadendHandler(); | |
} | |
function onloadendHandler(e) { | |
submit.disabled = false; | |
} | |
function uploadOnprogressHandler(e) { | |
if (e.lengthComputable) { | |
progressDiv.textContent = (e.loaded / e.total).toLocaleString(undefined,{style:'percent'}); | |
} | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment