各种报名都对照片有大小限制,鉴于这种情况,网上搜了后拼凑出了如下代码,用于解决1寸照片修改为2寸照片,同时将DPI修改为300,当然也可以根据自己的情况修改代码:
<input type="file" id="input" accept="image/*">
<div style="display: grid;grid-template-columns: 1fr 5px 1fr;">
<div>
<p>修改前:p>
<img id="before-image" src="" style="display: block; max-width:100%;" >
div>
<div>div>
<div>
<p>修改后(点击图片下载):p>
<a href="javascript:;" download><img id="after-image" src="" style="display: block;">a>
div>
div>
<script type="text/javascript">
const after_image = document.getElementById('before-image');
document.getElementById('input').addEventListener('change', (e) => {
const reader = new FileReader();
reader.readAsDataURL(e.target.files[0]);
reader.onload = (e) => {
b64 = e.target.result;
after_image.src = b64;
//设置目标图片大小
var target_width = 413;
var target_height = 626;
//计算目标图片宽高比例
var target_wh_scale = target_width / target_height;
//定义一个Image对象
var bitmap = new Image();
bitmap.src = b64;
bitmap.onload = function () {
//
var cut_width = 0;
var cut_height = 0;
//
var bitmap_wh_scale = bitmap.width / bitmap.height;
if (bitmap_wh_scale > target_wh_scale) {
cut_width = bitmap.width - bitmap.width / (bitmap_wh_scale / target_wh_scale);
}//裁剪宽度
else if (bitmap_wh_scale < target_wh_scale) {
cut_height = bitmap.height - bitmap.height * (bitmap_wh_scale / target_wh_scale);
} else {
Console.log("比例一致无需裁剪");
}
console.log("图片裁剪宽度:" + cut_width + " px");
console.log("图片裁剪高度:" + cut_height + " px");
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = target_width;
canvas.height = target_height;
ctx.drawImage(this, cut_width / 2, cut_height / 2, bitmap.width - cut_width, bitmap.height - cut_height, 0, 0, target_width, target_height);
var after_img = document.getElementById('after-image');
after_img.src = canvas.toDataURL('image/jpg');
var dataUrl = canvas.toDataURL('image/jpeg', 0.9);
//修改DPI为300
downloadBase64Img(after_img.parentElement, changeDpiDataUrl(dataUrl, 300), "t.jpg");
}
};
})
function downloadBase64Img(a, base64URL, fileName) {
// 将 a 标签的 download 属性设置为要下载的文件名
a.download = fileName || 'image';
// 创建 Blob 对象,并获取 base64 数据的 MIME 类型
const mimeType = base64URL.match(/:(.*?);/)[1];
// 将 base64 数据转换为字节数组
const byteCharacters = atob(base64URL.split(',')[1]);
const byteNumbers = new Array(byteCharacters.length);
// 将字节数组填充到 Uint8Array 中
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
// 创建 Blob 对象
const blob = new Blob([byteArray], { type: mimeType });
// 将 Blob 对象的 URL 赋值给 a 标签的 href 属性
a.href = URL.createObjectURL(blob);
}
function changeDpiDataUrl(base64Image, dpi) {
const PNG = 'image/png';
const JPEG = 'image/jpeg';
const dataSplitted = base64Image.split(',');
const format = dataSplitted[0];
const body = dataSplitted[1];
let type;
let headerLength;
let overwritepHYs = false;
if (format.indexOf(PNG) !== -1) {
type = PNG;
const b64Index = detectPhysChunkFromDataUrl(body);
// 28 bytes in dataUrl are 21bytes, length of phys chunk with everything inside.
if (b64Index >= 0) {
headerLength = Math.ceil((b64Index + 28) / 3) * 4;
overwritepHYs = true;
} else {
headerLength = 33 / 3 * 4;
}
}
if (format.indexOf(JPEG) !== -1) {
type = JPEG;
headerLength = 18 / 3 * 4;
}
// 33 bytes are ok for pngs and jpegs
// to contain the information.
const stringHeader = body.substring(0, headerLength);
const restOfData = body.substring(headerLength);
const headerBytes = atob(stringHeader);
const dataArray = new Uint8Array(headerBytes.length);
for (let i = 0; i < dataArray.length; i++) {
dataArray[i] = headerBytes.charCodeAt(i);
}
const finalArray = changeDpiOnArray(dataArray, dpi, type, overwritepHYs);
const base64Header = btoa(String.fromCharCode(...finalArray));
return [format, ',', base64Header, restOfData].join('');
}
function changeDpiOnArray(dataArray, dpi, format, overwritepHYs) {
const PNG = 'image/png';
const JPEG = 'image/jpeg';
if (format === JPEG) {
dataArray[13] = 1; // 1 pixel per inch or 2 pixel per cm
dataArray[14] = dpi >> 8; // dpiX high byte
dataArray[15] = dpi & 0xff; // dpiX low byte
dataArray[16] = dpi >> 8; // dpiY high byte
dataArray[17] = dpi & 0xff; // dpiY low byte
return dataArray;
}
if (format === PNG) {
const physChunk = new Uint8Array(13);
// chunk header pHYs
// 9 bytes of data
// 4 bytes of crc
// this multiplication is because the standard is dpi per meter.
const _P = 'p'.charCodeAt(0);
const _H = 'H'.charCodeAt(0);
const _Y = 'Y'.charCodeAt(0);
const _S = 's'.charCodeAt(0);
dpi *= 39.3701;
physChunk[0] = _P;
physChunk[1] = _H;
physChunk[2] = _Y;
physChunk[3] = _S;
physChunk[4] = dpi >>> 24; // dpiX highest byte
physChunk[5] = dpi >>> 16; // dpiX veryhigh byte
physChunk[6] = dpi >>> 8; // dpiX high byte
physChunk[7] = dpi & 0xff; // dpiX low byte
physChunk[8] = physChunk[4]; // dpiY highest byte
physChunk[9] = physChunk[5]; // dpiY veryhigh byte
physChunk[10] = physChunk[6]; // dpiY high byte
physChunk[11] = physChunk[7]; // dpiY low byte
physChunk[12] = 1; // dot per meter....
const crc = calcCrc(physChunk);
const crcChunk = new Uint8Array(4);
crcChunk[0] = crc >>> 24;
crcChunk[1] = crc >>> 16;
crcChunk[2] = crc >>> 8;
crcChunk[3] = crc & 0xff;
if (overwritepHYs) {
const startingIndex = searchStartOfPhys(dataArray);
dataArray.set(physChunk, startingIndex);
dataArray.set(crcChunk, startingIndex + 13);
return dataArray;
} else {
// i need to give back an array of data that is divisible by 3 so that
// dataurl encoding gives me integers, for luck this chunk is 17 + 4 = 21
// if it was we could add a text chunk contaning some info, untill desired
// length is met.
// chunk structur 4 bytes for length is 9
const chunkLength = new Uint8Array(4);
chunkLength[0] = 0;
chunkLength[1] = 0;
chunkLength[2] = 0;
chunkLength[3] = 9;
const finalHeader = new Uint8Array(54);
finalHeader.set(dataArray, 0);
finalHeader.set(chunkLength, 33);
finalHeader.set(physChunk, 37);
finalHeader.set(crcChunk, 50);
return finalHeader;
}
}
}
</script>
以上代码中changeDpiDataUrl()
和changeDpiOnArray()
两个函数来源于开源项目changeDPI。