1.完整的功能描述
功能概述
这是一个网页工具,支持用户输入不同格式的图片数据或上传本地图片文件,对图片进行预览、转换为多种格式,并支持导出不同格式的图片数据。
输入方式
-
文本输入 :用户可以输入 Data URL、公网图片 URL、Blob URL 或 Hex 字符串。支持的格式包括 data:image/...;base64 、 http(s)://xxx.jpg 、 blob:... 以及纯 Hex 字符串。
-
文件上传 :用户也能选择上传本地图片文件,支持常见的图片格式。 核心功能
-
预览功能 :用户点击"预览"按钮后,工具会根据输入内容尝试加载并显示图片。如果输入是网络图片,会处理 CORS 问题;若输入为 Hex 字符串,会将其转换为图片。
-
转换功能 :点击"转换"按钮,工具会将输入的图片数据转换为 Data URL、Blob 对象信息、ArrayBuffer(十进制字节数组)和 Hex 字符串,并显示在对应的文本框中。
-
导出功能 :
-
图片导出 :用户可以选择导出图片的格式(PNG、JPEG、WebP、GIF),点击"导出图片"按钮即可保存图片。
-
数据导出 :支持导出 Data URL、Blob 对象信息、ArrayBuffer 和 Hex 字符串,点击对应按钮可保存相应数据。 错误处理
当输入格式不支持、图片加载失败或转换出错时,工具会显示错误信息提示用户。同时,在加载过程中会显示加载状态,提升用户体验。
2.完整样式展示


3.完整代码演示
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>图片 ↔ 多格式预览、转换 & 导出</title>
<style>
:root {
--primary: #5c6bc0;
--light: #f5f5f5;
--dark: #424242;
--accent: #ff7043;
--radius: 8px;
--button-hover: #ff7043;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: "Helvetica Neue", Arial, sans-serif;
background-color: var(--light);
color: var(--dark);
display: flex;
justify-content: center;
padding: 40px 0;
}
.container {
width: 90%;
max-width: 800px;
background-color: #fff;
border-radius: var(--radius);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
padding: 24px;
}
h1 {
margin-bottom: 24px;
text-align: center;
color: var(--primary);
}
.note {
color: #e53935;
font-size: 0.9rem;
margin-bottom: 16px;
}
.section {
margin-bottom: 24px;
}
.section label {
display: block;
margin-bottom: 8px;
font-weight: bold;
}
textarea, select, input[type="text"] {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: var(--radius);
font-family: monospace;
background: #fafafa;
resize: vertical;
}
input[type="file"] {
display: block;
margin-top: 4px;
}
.buttons, .export-section {
text-align: center;
margin-top: 8px;
}
.buttons button, .export-btn {
background: var(--primary);
color: #fff;
border: none;
padding: 6px 12px;
border-radius: var(--radius);
cursor: pointer;
font-size: 0.9rem;
transition: background 0.3s;
margin: 4px;
}
.buttons button:hover, .export-btn:hover {
background: var(--button-hover);
}
#preview {
display: block;
max-width: 100%;
border-radius: var(--radius);
margin: 12px auto;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
textarea[readonly] {
background: #eee;
}
.field-with-btn {
position: relative;
}
.field-with-btn textarea {
margin-bottom: 8px;
}
.field-with-btn .export-btn {
position: absolute;
top: 8px;
right: 8px;
}
.format-select-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.format-select-wrapper select {
width: auto;
}
.error-message {
color: #e53935;
font-size: 0.9rem;
margin-top: 8px;
display: none;
}
.loading {
position: relative;
min-height: 100px;
}
.loading::after {
content: "🔄 加载中...";
display: block;
text-align: center;
color: var(--primary);
padding: 20px;
}
</style>
</head>
<body>
<div class="container">
<img src="data:image/png;base64,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" alt="Logo">
<h1>图片 ↔ 多格式预览、转换 & 导出</h1>
<div class="note">
⚠️ 请确保输入的是<b>图片文件的 URL</b>(例如 .jpg/.png/.webp 等),否则无法预览或转换。
<br>⚠️ 注意:网络图片需要支持CORS才能正常加载
</div>
<!-- Input and File Upload -->
<div class="section">
<label for="textInput">输入 Data URL、公网图片 URL、Blob URL 或 Hex 字符串</label>
<textarea id="textInput" rows="3" placeholder="支持 data:image/...;base64, http(s)://xxx.jpg, blob:..., 纯 Hex"></textarea>
<div id="inputError" class="error-message"></div>
</div>
<div class="section">
<label for="fileInput">或 上传 本地文件</label>
<input type="file" id="fileInput" accept="image/*">
</div>
<div class="buttons">
<button id="previewBtn">预览</button>
<button id="convertBtn">转换</button>
<button id="clearBtn" style="background:#999;">清空</button>
</div>
<!-- Image Preview -->
<div class="section loading" id="previewContainer">
<label>图片预览</label>
<img id="preview" alt="预览图像" style="display:none;" crossorigin="anonymous">
</div>
<!-- Format Selection and Export -->
<div class="section export-section">
<label>导出预览图像</label><br>
<div class="format-select-wrapper">
<select id="formatSelect">
<option value="image/png">PNG</option>
<option value="image/jpeg">JPEG</option>
<option value="image/webp">WebP</option>
<option value="image/gif">GIF</option>
</select>
<button id="exportImageBtn" class="export-btn">导出图片</button>
</div>
</div>
<!-- Data URL Export -->
<div class="section field-with-btn">
<label for="outDataUrl">Data URL(Base64)</label>
<textarea id="outDataUrl" rows="2" readonly></textarea>
<button class="export-btn" id="exportDataUrlBtn">导出 Data URL</button>
</div>
<!-- Blob Export -->
<div class="section field-with-btn">
<label>Blob 对象 信息</label>
<textarea id="outBlob" rows="2" readonly placeholder="显示 Blob 的 size/type"></textarea>
<button class="export-btn" id="exportBlobInfoBtn">导出 Blob 信息</button>
</div>
<!-- ArrayBuffer Export -->
<div class="section field-with-btn">
<label for="outArrayBuffer">ArrayBuffer(十进制字节数组)</label>
<textarea id="outArrayBuffer" rows="3" readonly></textarea>
<button class="export-btn" id="exportArrayBufferBtn">导出 ArrayBuffer</button>
</div>
<!-- Hex Export -->
<div class="section field-with-btn">
<label for="outHex">Hex 字符串</label>
<textarea id="outHex" rows="3" readonly></textarea>
<button class="export-btn" id="exportHexBtn">导出 Hex</button>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM元素引用
const elements = {
textInput: document.getElementById('textInput'),
fileInput: document.getElementById('fileInput'),
previewBtn: document.getElementById('previewBtn'),
convertBtn: document.getElementById('convertBtn'),
clearBtn: document.getElementById('clearBtn'),
preview: document.getElementById('preview'),
formatSelect: document.getElementById('formatSelect'),
exportImageBtn: document.getElementById('exportImageBtn'),
outDataUrl: document.getElementById('outDataUrl'),
outBlob: document.getElementById('outBlob'),
outArrayBuffer: document.getElementById('outArrayBuffer'),
outHex: document.getElementById('outHex'),
previewContainer: document.getElementById('previewContainer'),
inputError: document.getElementById('inputError')
};
let currentBlob = null;
let currentDataUrl = null;
// 显示错误信息
function showError(message) {
elements.inputError.textContent = message;
elements.inputError.style.display = 'block';
elements.previewContainer.classList.remove('loading');
}
// 隐藏错误信息
function hideError() {
elements.inputError.style.display = 'none';
}
// 重置功能
function resetAll() {
elements.textInput.value = '';
elements.fileInput.value = '';
elements.preview.style.display = 'none';
elements.outDataUrl.value = '';
elements.outBlob.value = '';
elements.outArrayBuffer.value = '';
elements.outHex.value = '';
currentBlob = null;
currentDataUrl = null;
hideError();
elements.previewContainer.classList.remove('loading');
if (elements.preview.src.startsWith('blob:')) {
URL.revokeObjectURL(elements.preview.src);
}
}
// 处理CORS代理请求
async function fetchWithCORS(url) {
try {
const response = await fetch(url, {
mode: 'cors',
credentials: 'omit'
});
if (!response.ok) {
throw new Error('无法加载图片');
}
return response;
} catch (error) {
throw new Error(`无法加载图片: ${error.message}`);
}
}
// 预览功能
async function previewInput() {
try {
elements.previewContainer.classList.add('loading');
hideError();
const input = elements.textInput.value.trim();
const file = elements.fileInput.files[0];
if (input) {
if (/^data:image\//i.test(input)) { // Data URL
currentDataUrl = input;
const img = new Image();
img.onload = async () => {
elements.preview.src = currentDataUrl;
elements.preview.style.display = 'block';
elements.previewContainer.classList.remove('loading');
// 调用转换功能以更新数据显示
await convertInput();
};
img.onerror = () => {
throw new Error('无效的Data URL图片');
};
img.src = currentDataUrl;
} else if (/^https?:\/\//i.test(input)) { // 网络URL
const img = new Image();
img.crossOrigin = "Anonymous";
img.onload = async () => {
elements.preview.src = input;
elements.preview.style.display = 'block';
elements.previewContainer.classList.remove('loading');
// 调用转换功能以更新数据显示
await convertInput();
};
img.onerror = async () => {
try {
const response = await fetchWithCORS(input);
const blob = await response.blob();
const url = URL.createObjectURL(blob);
elements.preview.src = url;
elements.preview.style.display = 'block';
elements.previewContainer.classList.remove('loading');
// 调用转换功能以更新数据显示
await convertInput();
} catch (error) {
throw new Error('图片加载失败');
}
};
img.src = input;
} else if (/^blob:/i.test(input)) { // Blob URL
elements.preview.src = input;
elements.preview.style.display = 'block';
elements.previewContainer.classList.remove('loading');
// 调用转换功能以更新数据显示
await convertInput();
} else if (/^[0-9a-f\s]+$/i.test(input)) { // Hex字符串
// 直接调用转换功能处理Hex字符串
await convertInput();
} else {
throw new Error('不支持的输入格式');
}
} else if (file) { // 本地文件
const url = URL.createObjectURL(file);
elements.preview.src = url;
elements.preview.style.display = 'block';
elements.previewContainer.classList.remove('loading');
// 调用转换功能以更新数据显示
await convertInput();
} else {
throw new Error('请输入内容或上传文件');
}
} catch (error) {
console.error('预览错误:', error);
showError(`预览错误: ${error.message}`);
elements.preview.style.display = 'none';
elements.previewContainer.classList.remove('loading');
}
}
// 转换功能
async function convertInput() {
try {
elements.previewContainer.classList.add('loading');
hideError();
const input = elements.textInput.value.trim();
const file = elements.fileInput.files[0];
let blob, buffer;
if (input) {
if (/^data:image\//i.test(input)) { // Data URL
currentDataUrl = input;
blob = await fetch(currentDataUrl).then(r => r.blob());
} else if (/^https?:\/\//i.test(input)) { // 网络URL
const response = await fetchWithCORS(input);
blob = await response.blob();
// 验证图片类型
if (!blob.type.startsWith('image/')) {
throw new Error('URL指向的不是图片文件');
}
} else if (/^blob:/i.test(input)) { // Blob URL
const response = await fetch(input);
blob = await response.blob();
} else if (/^[0-9a-f\s]+$/i.test(input)) { // Hex字符串
let cleanHex = input.replace(/\s+/g, '');
if (cleanHex.length % 2 !== 0) throw new Error('Hex长度无效');
buffer = new Uint8Array(cleanHex.match(/../g).map(h => parseInt(h, 16)));
blob = new Blob([buffer], { type: 'image/*' });
} else {
throw new Error('不支持的输入格式');
}
} else if (file) { // 本地文件
blob = file;
} else {
throw new Error('请输入内容或上传文件');
}
if (!blob) {
throw new Error('无法创建Blob对象');
}
// 更新数据展示
currentBlob = blob;
buffer = await blob.arrayBuffer();
// 生成Data URL
currentDataUrl = await blobToDataURL(blob);
elements.preview.src = currentDataUrl;
elements.preview.style.display = 'block';
elements.outDataUrl.value = currentDataUrl;
elements.outBlob.value = `size: ${blob.size} bytes\ntype: ${blob.type || '未知类型'}`;
const uint8 = new Uint8Array(buffer);
elements.outArrayBuffer.value = JSON.stringify([...uint8]);
elements.outHex.value = [...uint8].map(b => b.toString(16).padStart(2, '0')).join('');
elements.previewContainer.classList.remove('loading');
} catch (error) {
console.error('转换错误:', error);
showError(`转换错误: ${error.message}`);
elements.preview.style.display = 'none';
elements.previewContainer.classList.remove('loading');
}
}
// 工具函数:Blob转DataURL
function blobToDataURL(blob) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
}
// 导出图片功能
elements.exportImageBtn.addEventListener('click', async () => {
if (!currentDataUrl) {
showError('请先转换图片');
return;
}
try {
const img = new Image();
img.crossOrigin = "Anonymous";
img.src = currentDataUrl;
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = () => reject(new Error('图片加载失败'));
});
const canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const type = elements.formatSelect.value;
canvas.toBlob(blob => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `image_export.${type.split('/')[1] || 'png'}`;
a.click();
setTimeout(() => {
URL.revokeObjectURL(url);
URL.revokeObjectURL(img.src);
}, 1000);
}, type, 0.9);
} catch (error) {
console.error('导出图片错误:', error);
showError(`导出图片错误: ${error.message}`);
}
});
// 通用导出处理
function setupExportButton(buttonId, contentFieldId, filename) {
const button = document.getElementById(buttonId);
const contentField = document.getElementById(contentFieldId);
button.addEventListener('click', () => {
const content = contentField.value;
if (!content) {
showError('无可用数据');
return;
}
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
setTimeout(() => URL.revokeObjectURL(url), 1000);
});
}
// 绑定导出功能
setupExportButton('exportDataUrlBtn', 'outDataUrl', 'dataurl.txt');
setupExportButton('exportBlobInfoBtn', 'outBlob', 'blob_info.txt');
setupExportButton('exportArrayBufferBtn', 'outArrayBuffer', 'arraybuffer.json');
setupExportButton('exportHexBtn', 'outHex', 'hex.txt');
// 事件监听
elements.previewBtn.addEventListener('click', previewInput);
elements.convertBtn.addEventListener('click', convertInput);
elements.clearBtn.addEventListener('click', resetAll);
elements.textInput.addEventListener('input', hideError);
elements.fileInput.addEventListener('change', hideError);
});
</script>
</body>
</html>
<style>
:root {
--primary: #5c6bc0;
--light: #f5f5f5;
--dark: #424242;
--accent: #ff7043;
--radius: 12px;
--button-hover: #ff7043;
--transition: all 0.3s ease;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: "Helvetica Neue", Arial, sans-serif;
background-color: var(--light);
color: var(--dark);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.container {
width: 90%;
max-width: 1000px;
background-color: #fff;
border-radius: var(--radius);
box-shadow: 0 6px 30px rgba(0, 0, 0, 0.1);
padding: 32px;
transition: var(--transition);
}
.container:hover {
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.15);
}
h1 {
margin-bottom: 32px;
text-align: center;
color: var(--primary);
font-size: 2.2rem;
}
.note {
color: #e53935;
font-size: 1rem;
margin-bottom: 24px;
padding: 12px;
background-color: #ffebee;
border-radius: var(--radius);
}
.section {
margin-bottom: 32px;
padding: 16px;
background-color: #fafafa;
border-radius: var(--radius);
transition: var(--transition);
}
.section:hover {
background-color: #f5f5f5;
}
.section label {
display: block;
margin-bottom: 12px;
font-weight: bold;
color: var(--primary);
}
textarea, select, input[type="text"] {
width: 100%;
padding: 14px;
border: 1px solid #ddd;
border-radius: var(--radius);
font-family: monospace;
background: #fff;
resize: vertical;
transition: var(--transition);
}
textarea:focus, select:focus, input[type="text"]:focus {
border-color: var(--primary);
outline: none;
box-shadow: 0 0 0 3px rgba(92, 107, 192, 0.2);
}
input[type="file"] {
display: block;
margin-top: 8px;
padding: 8px 0;
}
.buttons, .export-section {
text-align: center;
margin-top: 16px;
}
.buttons button, .export-btn {
background: var(--primary);
color: #fff;
border: none;
padding: 10px 20px;
border-radius: var(--radius);
cursor: pointer;
font-size: 1rem;
transition: var(--transition);
margin: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.buttons button:hover, .export-btn:hover {
background: var(--button-hover);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
#preview {
display: block;
max-width: 100%;
border-radius: var(--radius);
margin: 16px auto;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
transition: var(--transition);
}
#preview:hover {
transform: scale(1.02);
}
textarea[readonly] {
background: #f8f8f8;
color: #666;
}
.field-with-btn {
position: relative;
}
.field-with-btn textarea {
margin-bottom: 12px;
}
.field-with-btn .export-btn {
position: absolute;
top: 12px;
right: 12px;
padding: 6px 12px;
font-size: 0.9rem;
}
.format-select-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
}
.format-select-wrapper select {
width: auto;
min-width: 120px;
}
.error-message {
color: #e53935;
font-size: 1rem;
margin-top: 12px;
display: none;
padding: 8px;
background-color: #ffebee;
border-radius: var(--radius);
}
.loading {
position: relative;
min-height: 150px;
}
.loading::after {
content: "🔄 加载中...";
display: block;
text-align: center;
color: var(--primary);
padding: 30px;
font-size: 1.2rem;
}
</style>