update
This commit is contained in:
@@ -0,0 +1,577 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect, useRef, ChangeEvent } from 'react';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faDownload, faTrash, faImage, faCopy, faCheck, faExchangeAlt, faUpload } from '@fortawesome/free-solid-svg-icons';
|
||||
import ToolHeader from '@/components/ToolHeader';
|
||||
import { useLanguage } from '@/context/LanguageContext';
|
||||
import tools from '@/config/tools';
|
||||
import BackToTop from '@/components/BackToTop';
|
||||
|
||||
// 添加CSS变量样式
|
||||
const styles = {
|
||||
container: "min-h-screen flex flex-col max-w-[1440px] mx-auto p-4 md:p-6",
|
||||
card: "card p-6 mb-6",
|
||||
inputLabel: "block text-secondary text-sm font-bold mb-2",
|
||||
heading: "text-lg font-semibold mb-2 text-primary",
|
||||
textArea: "search-input w-full h-56 font-mono text-sm",
|
||||
switchBtn: "btn-secondary flex items-center justify-center my-4 mx-auto",
|
||||
imageContainer: "bg-block rounded-lg p-4 min-h-64 flex items-center justify-center border border-purple-glow/15 mt-4",
|
||||
image: "max-w-full max-h-96 object-contain",
|
||||
placeholder: "text-tertiary",
|
||||
actionBtn: "w-full",
|
||||
actionBtnDisabled: "btn-secondary opacity-50 cursor-not-allowed w-full",
|
||||
errorMsg: "mt-2 text-red-500 text-sm",
|
||||
successMsg: "mt-2 text-green-500 text-sm",
|
||||
copyBtn: "btn-secondary text-sm py-1 px-3 flex items-center",
|
||||
fileUploadBtn: "btn-primary flex items-center justify-center cursor-pointer",
|
||||
fileName: "ml-3 text-secondary text-sm",
|
||||
tabs: "flex mb-6 border-b border-purple-glow/20",
|
||||
tab: "py-2 px-4 cursor-pointer text-secondary border-b-2 border-transparent",
|
||||
activeTab: "py-2 px-4 text-primary border-b-2 border-primary font-medium",
|
||||
};
|
||||
|
||||
// 转换模式类型
|
||||
type ConversionMode = 'base64_to_image' | 'image_to_base64';
|
||||
|
||||
export default function Base64ImageConverter() {
|
||||
const { t } = useLanguage();
|
||||
|
||||
// 从工具配置中获取当前工具图标
|
||||
const toolIcon = tools.find(tool => tool.code === 'base64_to_image')?.icon || faImage;
|
||||
|
||||
// 状态管理
|
||||
const [conversionMode, setConversionMode] = useState<ConversionMode>('base64_to_image');
|
||||
|
||||
// Base64转图片状态
|
||||
const [base64Input, setBase64Input] = useState('');
|
||||
const [imageOutput, setImageOutput] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [mimeType, setMimeType] = useState<string>('');
|
||||
const [fileName, setFileName] = useState<string>('image');
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
// 图片转Base64状态
|
||||
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
||||
const [uploadedImage, setUploadedImage] = useState<string | null>(null);
|
||||
const [base64Output, setBase64Output] = useState<string>('');
|
||||
const [rawBase64Output, setRawBase64Output] = useState<string>('');
|
||||
const [outputWithPrefix, setOutputWithPrefix] = useState<boolean>(true);
|
||||
const [uploadError, setUploadError] = useState<string | null>(null);
|
||||
const [uploadSuccess, setUploadSuccess] = useState<string | null>(null);
|
||||
const [base64Copied, setBase64Copied] = useState(false);
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// 当Base64输入变化时处理转换(Base64转图片)
|
||||
useEffect(() => {
|
||||
if (conversionMode !== 'base64_to_image' || !base64Input.trim()) {
|
||||
setImageOutput(null);
|
||||
setError(null);
|
||||
setMimeType('');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 清除可能的数据URL前缀
|
||||
const processedInput = base64Input.trim();
|
||||
|
||||
// 检查是否已经是完整的数据URL
|
||||
if (processedInput.startsWith('data:')) {
|
||||
// 从数据URL中提取MIME类型
|
||||
const mimeMatch = processedInput.match(/^data:([^;]+);base64,/);
|
||||
if (mimeMatch) {
|
||||
setMimeType(mimeMatch[1]);
|
||||
// 使用完整的数据URL
|
||||
setImageOutput(processedInput);
|
||||
setError(null);
|
||||
|
||||
// 根据MIME类型设置合适的文件扩展名
|
||||
updateFileNameFromMimeType(mimeMatch[1]);
|
||||
} else {
|
||||
throw new Error(t('tools.base64_to_image.invalid_data_url'));
|
||||
}
|
||||
} else {
|
||||
// 假设这是原始Base64,需要添加数据URL前缀
|
||||
// 尝试推断MIME类型或使用默认值
|
||||
const inferredMimeType = inferMimeTypeFromBase64(processedInput);
|
||||
setMimeType(inferredMimeType);
|
||||
|
||||
// 创建完整的数据URL
|
||||
const dataUrl = `data:${inferredMimeType};base64,${processedInput}`;
|
||||
setImageOutput(dataUrl);
|
||||
setError(null);
|
||||
|
||||
// 根据MIME类型设置合适的文件扩展名
|
||||
updateFileNameFromMimeType(inferredMimeType);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error processing base64:', err);
|
||||
setError((err as Error).message || t('tools.base64_to_image.processing_error'));
|
||||
setImageOutput(null);
|
||||
}
|
||||
}, [base64Input, conversionMode, t]);
|
||||
|
||||
// 从Base64数据推断MIME类型的简单逻辑
|
||||
const inferMimeTypeFromBase64 = (base64String: string): string => {
|
||||
// 默认为PNG
|
||||
let mimeType = 'image/png';
|
||||
|
||||
// 检查前几个字符以尝试推断类型
|
||||
// 注意:这是一个非常简化的版本,实际生产中可能需要更复杂的逻辑
|
||||
const firstChars = base64String.substring(0, 4);
|
||||
|
||||
if (firstChars.startsWith('/9j/')) {
|
||||
mimeType = 'image/jpeg';
|
||||
} else if (firstChars.startsWith('iVBO') || firstChars.startsWith('IVBO')) {
|
||||
mimeType = 'image/png';
|
||||
} else if (firstChars.startsWith('R0lG')) {
|
||||
mimeType = 'image/gif';
|
||||
} else if (firstChars.startsWith('UE5H')) {
|
||||
mimeType = 'image/png';
|
||||
} else if (firstChars.startsWith('Qk0=')) {
|
||||
mimeType = 'image/bmp';
|
||||
} else if (firstChars.startsWith('PHN2')) {
|
||||
mimeType = 'image/svg+xml';
|
||||
} else if (firstChars.startsWith('AAAA')) {
|
||||
mimeType = 'image/webp';
|
||||
}
|
||||
|
||||
return mimeType;
|
||||
};
|
||||
|
||||
// 根据MIME类型更新文件名的扩展名
|
||||
const updateFileNameFromMimeType = (mime: string) => {
|
||||
let extension = 'png'; // 默认扩展名
|
||||
|
||||
// 根据MIME类型设置扩展名
|
||||
if (mime === 'image/jpeg' || mime === 'image/jpg') {
|
||||
extension = 'jpg';
|
||||
} else if (mime === 'image/png') {
|
||||
extension = 'png';
|
||||
} else if (mime === 'image/gif') {
|
||||
extension = 'gif';
|
||||
} else if (mime === 'image/bmp') {
|
||||
extension = 'bmp';
|
||||
} else if (mime === 'image/svg+xml') {
|
||||
extension = 'svg';
|
||||
} else if (mime === 'image/webp') {
|
||||
extension = 'webp';
|
||||
}
|
||||
|
||||
setFileName(`image.${extension}`);
|
||||
};
|
||||
|
||||
// 清空输入和输出(Base64转图片)
|
||||
const clearBase64ToImage = () => {
|
||||
setBase64Input('');
|
||||
setImageOutput(null);
|
||||
setError(null);
|
||||
setMimeType('');
|
||||
setFileName('image');
|
||||
};
|
||||
|
||||
// 清空输入和输出(图片转Base64)
|
||||
const clearImageToBase64 = () => {
|
||||
setUploadedFile(null);
|
||||
setUploadedImage(null);
|
||||
setBase64Output('');
|
||||
setRawBase64Output('');
|
||||
setUploadError(null);
|
||||
setUploadSuccess(null);
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
// 下载图片
|
||||
const downloadImage = () => {
|
||||
if (!imageOutput) return;
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = imageOutput;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
|
||||
// 加载示例Base64
|
||||
const loadExample = () => {
|
||||
// 一个简单的彩色渐变PNG图像的Base64
|
||||
const exampleBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TtSIVByuIOGSoThZERRylikWwUNoKrTqYXPohNGlIUlwcBdeCgx+LVQcXZ10dXAVB8APE1cVJ0UVK/F9SaBHjwXE/3t173L0DhGaVqWbPOKBqlpFOxMVcflUMvCKIEMKIISgxU5+TZRm4jq97+Ph6F+NZ3uf+HANKwWSATySeY7phEW8QT29aOud94ggrSQrxOfGYQRckfuS67PIb55LDAs8MGZnUPHGEWCx1sdzFrGKoxFPEUUXVKN+fc1nhvMVZrdZZ+578hcGCtpLhOs1RJLCEJFIQIaOOCqqwEKNVI8VEmvbjHv4Rx58il0yuChg5FlCDCsnxg//B727N0uSEmxROAH0vtv0xAoR2gVbDtr+Pbbt1AgSegSut499oAjOfpDc6WuwIGNwGLq47mrIHXO4AQ0+GbMqO5KcpFIvA+xl9Uw4YuAUCa25vzX2cPgBpamX5Nxg4AiJlyl73eHdPd2//nmn39wNZjHKYJHtTZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+cMEwwTIg9nvK0AAACbSURBVHja7dAxAQAgDMAwwL/n8SSBJ2uGLdite3YEhhCGEIYQhhCGEIYQhhCGEIYQhhCGEIYQhhCGEIYQhhCGEIYQhhCGEIYQhhCGEIYQhhCGEIYQhhCGEIYQhhCGEIYQhhCGEIYQhhCGEIYQhhCGEIYQhhCGEIYQhhCGEIYQhhCGEH8tC+O6BRrHuUAeAAAAAElFTkSuQmCC';
|
||||
|
||||
setBase64Input(exampleBase64);
|
||||
};
|
||||
|
||||
// 复制现有Base64到剪贴板(Base64转图片)
|
||||
const copyBase64 = () => {
|
||||
if (!base64Input) return;
|
||||
|
||||
navigator.clipboard.writeText(base64Input)
|
||||
.then(() => {
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to copy:', err);
|
||||
setError(t('tools.base64_to_image.copy_failed'));
|
||||
});
|
||||
};
|
||||
|
||||
// 复制生成的Base64到剪贴板(图片转Base64)
|
||||
const copyGeneratedBase64 = () => {
|
||||
if (!base64Output) return;
|
||||
|
||||
navigator.clipboard.writeText(base64Output)
|
||||
.then(() => {
|
||||
setBase64Copied(true);
|
||||
setTimeout(() => setBase64Copied(false), 2000);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to copy:', err);
|
||||
setUploadError(t('tools.base64_to_image.copy_failed'));
|
||||
});
|
||||
};
|
||||
|
||||
// 切换转换模式
|
||||
const toggleConversionMode = () => {
|
||||
setConversionMode(prevMode =>
|
||||
prevMode === 'base64_to_image' ? 'image_to_base64' : 'base64_to_image'
|
||||
);
|
||||
|
||||
// 清空各自的状态
|
||||
clearBase64ToImage();
|
||||
clearImageToBase64();
|
||||
};
|
||||
|
||||
// 处理文件上传(图片转Base64)
|
||||
const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
// 检查文件类型
|
||||
if (!file.type.startsWith('image/')) {
|
||||
setUploadError(t('tools.base64_to_image.invalid_image_file'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查文件大小(限制为10MB)
|
||||
if (file.size > 10 * 1024 * 1024) {
|
||||
setUploadError(t('tools.base64_to_image.file_too_large'));
|
||||
return;
|
||||
}
|
||||
|
||||
setUploadedFile(file);
|
||||
setUploadError(null);
|
||||
|
||||
// 读取并显示图片预览
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const result = e.target?.result as string;
|
||||
setUploadedImage(result);
|
||||
|
||||
// 提取Base64部分
|
||||
const base64WithPrefix = result;
|
||||
const base64Raw = result.split(',')[1];
|
||||
|
||||
setRawBase64Output(base64Raw);
|
||||
setBase64Output(outputWithPrefix ? base64WithPrefix : base64Raw);
|
||||
|
||||
setUploadSuccess(t('tools.base64_to_image.conversion_success'));
|
||||
};
|
||||
reader.onerror = () => {
|
||||
setUploadError(t('tools.base64_to_image.file_reading_error'));
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
// 切换输出格式(是否包含前缀)
|
||||
const toggleOutputFormat = () => {
|
||||
setOutputWithPrefix(!outputWithPrefix);
|
||||
setBase64Output(outputWithPrefix ? rawBase64Output : (uploadedImage || ''));
|
||||
};
|
||||
|
||||
// 保存Base64内容为文本文件
|
||||
const saveBase64AsTextFile = () => {
|
||||
if (!base64Output) return;
|
||||
|
||||
const blob = new Blob([base64Output], { type: 'text/plain' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = uploadedFile ? `${uploadedFile.name}.txt` : 'base64_data.txt';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{/* 工具标题 */}
|
||||
<ToolHeader
|
||||
icon={toolIcon}
|
||||
toolCode="base64_to_image"
|
||||
title=""
|
||||
description=""
|
||||
/>
|
||||
|
||||
{/* 切换按钮 */}
|
||||
<button
|
||||
className={styles.switchBtn}
|
||||
onClick={toggleConversionMode}
|
||||
>
|
||||
<FontAwesomeIcon icon={faExchangeAlt} className="mr-2 icon" />
|
||||
{conversionMode === 'base64_to_image'
|
||||
? t('tools.base64_to_image.switch_to_image_to_base64')
|
||||
: t('tools.base64_to_image.switch_to_base64_to_image')
|
||||
}
|
||||
</button>
|
||||
|
||||
{/* 选项卡 */}
|
||||
<div className={styles.tabs}>
|
||||
<div
|
||||
className={conversionMode === 'base64_to_image' ? styles.activeTab : styles.tab}
|
||||
onClick={() => setConversionMode('base64_to_image')}
|
||||
>
|
||||
{t('tools.base64_to_image.base64_to_image_tab')}
|
||||
</div>
|
||||
<div
|
||||
className={conversionMode === 'image_to_base64' ? styles.activeTab : styles.tab}
|
||||
onClick={() => setConversionMode('image_to_base64')}
|
||||
>
|
||||
{t('tools.base64_to_image.image_to_base64_tab')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Base64转图片 */}
|
||||
{conversionMode === 'base64_to_image' && (
|
||||
<div className="flex flex-col lg:flex-row gap-6">
|
||||
{/* 输入区域 */}
|
||||
<div className="w-full lg:w-1/2">
|
||||
<div className={styles.card}>
|
||||
<h2 className={styles.heading}>{t('tools.base64_to_image.input_title')}</h2>
|
||||
<label className={styles.inputLabel}>{t('tools.base64_to_image.base64_input')}</label>
|
||||
<textarea
|
||||
className={styles.textArea}
|
||||
value={base64Input}
|
||||
onChange={(e) => setBase64Input(e.target.value)}
|
||||
placeholder={t('tools.base64_to_image.input_placeholder')}
|
||||
spellCheck="false"
|
||||
/>
|
||||
|
||||
{error && <div className={styles.errorMsg}>{error}</div>}
|
||||
|
||||
<div className="flex items-center justify-between mt-4">
|
||||
<button
|
||||
onClick={loadExample}
|
||||
className="btn-secondary"
|
||||
>
|
||||
{t('tools.base64_to_image.load_example')}
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={copyBase64}
|
||||
disabled={!base64Input}
|
||||
className={!base64Input ? styles.actionBtnDisabled : "btn-secondary"}
|
||||
>
|
||||
<FontAwesomeIcon icon={copied ? faCheck : faCopy} className="mr-2 icon" />
|
||||
{copied ? t('common.copySuccess') : t('tools.base64_to_image.copy')}
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={clearBase64ToImage}
|
||||
className="btn-secondary"
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} className="mr-2 icon" />
|
||||
{t('tools.base64_to_image.clear_all')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 输出区域 */}
|
||||
<div className="w-full lg:w-1/2">
|
||||
<div className={styles.card}>
|
||||
<h2 className={styles.heading}>{t('tools.base64_to_image.output_title')}</h2>
|
||||
|
||||
<div className={styles.imageContainer}>
|
||||
{imageOutput ? (
|
||||
<img
|
||||
src={imageOutput}
|
||||
alt={t('tools.base64_to_image.converted_image')}
|
||||
className={styles.image}
|
||||
/>
|
||||
) : (
|
||||
<div className={styles.placeholder}>
|
||||
{t('tools.base64_to_image.no_image')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{imageOutput && (
|
||||
<div className="mt-4">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-secondary text-sm">{t('tools.base64_to_image.image_type')}: {mimeType}</span>
|
||||
<input
|
||||
type="text"
|
||||
className="search-input w-48"
|
||||
value={fileName}
|
||||
onChange={(e) => setFileName(e.target.value)}
|
||||
placeholder={t('tools.base64_to_image.file_name')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={downloadImage}
|
||||
className="btn-primary w-full"
|
||||
disabled={!imageOutput}
|
||||
>
|
||||
<FontAwesomeIcon icon={faDownload} className="mr-2 icon" />
|
||||
{t('tools.base64_to_image.download_image')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 图片转Base64 */}
|
||||
{conversionMode === 'image_to_base64' && (
|
||||
<div className="flex flex-col lg:flex-row gap-6">
|
||||
{/* 输入区域 */}
|
||||
<div className="w-full lg:w-1/2">
|
||||
<div className={styles.card}>
|
||||
<h2 className={styles.heading}>{t('tools.base64_to_image.image_input_title')}</h2>
|
||||
|
||||
<div className="mb-6">
|
||||
<label className={styles.inputLabel}>
|
||||
{t('tools.base64_to_image.select_image')}
|
||||
</label>
|
||||
<div className="flex items-center">
|
||||
<label className={styles.fileUploadBtn}>
|
||||
<FontAwesomeIcon icon={faUpload} className="mr-2 icon" />
|
||||
{t('tools.base64_to_image.choose_file')}
|
||||
<input
|
||||
type="file"
|
||||
className="hidden"
|
||||
accept="image/*"
|
||||
onChange={handleFileChange}
|
||||
ref={fileInputRef}
|
||||
/>
|
||||
</label>
|
||||
{uploadedFile && (
|
||||
<span className={styles.fileName}>{uploadedFile.name}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{uploadError && <div className={styles.errorMsg}>{uploadError}</div>}
|
||||
{uploadSuccess && <div className={styles.successMsg}>{uploadSuccess}</div>}
|
||||
|
||||
<div className={styles.imageContainer}>
|
||||
{uploadedImage ? (
|
||||
<img
|
||||
src={uploadedImage}
|
||||
alt={t('tools.base64_to_image.uploaded_image')}
|
||||
className={styles.image}
|
||||
/>
|
||||
) : (
|
||||
<div className={styles.placeholder}>
|
||||
{t('tools.base64_to_image.no_uploaded_image')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-end mt-4">
|
||||
<button
|
||||
onClick={clearImageToBase64}
|
||||
className="btn-secondary"
|
||||
disabled={!uploadedFile}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} className="mr-2 icon" />
|
||||
{t('tools.base64_to_image.clear_all')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 输出区域 */}
|
||||
<div className="w-full lg:w-1/2">
|
||||
<div className={styles.card}>
|
||||
<h2 className={styles.heading}>{t('tools.base64_to_image.base64_output_title')}</h2>
|
||||
|
||||
<div className="flex items-center mb-4">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="includePrefix"
|
||||
checked={outputWithPrefix}
|
||||
onChange={toggleOutputFormat}
|
||||
className="mr-2 accent-[rgb(var(--color-primary))] w-4 h-4"
|
||||
/>
|
||||
<label htmlFor="includePrefix" className="text-secondary text-sm">
|
||||
{t('tools.base64_to_image.include_prefix')}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<textarea
|
||||
className={styles.textArea}
|
||||
value={base64Output}
|
||||
readOnly
|
||||
placeholder={t('tools.base64_to_image.output_placeholder')}
|
||||
spellCheck="false"
|
||||
/>
|
||||
|
||||
{uploadedFile && base64Output && (
|
||||
<div className="flex items-center justify-between mt-4">
|
||||
<button
|
||||
onClick={copyGeneratedBase64}
|
||||
className="btn-secondary"
|
||||
>
|
||||
<FontAwesomeIcon icon={base64Copied ? faCheck : faCopy} className="mr-2 icon" />
|
||||
{base64Copied ? t('common.copySuccess') : t('tools.base64_to_image.copy_base64')}
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={saveBase64AsTextFile}
|
||||
className="btn-primary"
|
||||
>
|
||||
<FontAwesomeIcon icon={faDownload} className="mr-2 icon" />
|
||||
{t('tools.base64_to_image.save_as_text')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 使用说明 */}
|
||||
<div className="card p-4 mb-6 mt-6">
|
||||
<h3 className="text-md font-medium text-primary mb-2">{t('tools.base64_to_image.usage_guide')}</h3>
|
||||
<ul className="list-disc list-inside text-sm space-y-1 text-tertiary">
|
||||
{conversionMode === 'base64_to_image' ? (
|
||||
<>
|
||||
<li>{t('tools.base64_to_image.guide_1')}</li>
|
||||
<li>{t('tools.base64_to_image.guide_2')}</li>
|
||||
<li>{t('tools.base64_to_image.guide_3')}</li>
|
||||
<li>{t('tools.base64_to_image.guide_4')}</li>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<li>{t('tools.base64_to_image.guide_5')}</li>
|
||||
<li>{t('tools.base64_to_image.guide_6')}</li>
|
||||
<li>{t('tools.base64_to_image.guide_7')}</li>
|
||||
<li>{t('tools.base64_to_image.guide_8')}</li>
|
||||
</>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* 回到顶部按钮 */}
|
||||
<BackToTop />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -27,7 +27,8 @@ export const en = {
|
||||
unicode_converter: tools.unicode_converter.en,
|
||||
url_encoder: tools.url_encoder.en,
|
||||
number_base_converter: tools.number_base_converter.en,
|
||||
yml_properties_converter: tools.yml_properties_converter.en
|
||||
yml_properties_converter: tools.yml_properties_converter.en,
|
||||
base64_to_image: tools.base64_to_image.en
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
export const base64ToImageEn = {
|
||||
title: 'Base64 Image Converter',
|
||||
description: 'Convert between Base64 string and image',
|
||||
|
||||
// Base64 to Image
|
||||
input_title: 'Input Base64 String',
|
||||
output_title: 'Image Output',
|
||||
base64_input: 'Base64 String',
|
||||
input_placeholder: 'Paste your Base64 encoded image string here...',
|
||||
load_example: 'Load Example',
|
||||
clear_all: 'Clear',
|
||||
copy: 'Copy Base64',
|
||||
download_image: 'Download Image',
|
||||
converted_image: 'Converted Image',
|
||||
no_image: 'No image to display yet. Paste a Base64 string to convert.',
|
||||
processing_error: 'Error processing Base64 string',
|
||||
invalid_data_url: 'Invalid data URL format',
|
||||
invalid_base64: 'Invalid Base64 encoded string',
|
||||
copy_failed: 'Failed to copy to clipboard',
|
||||
image_type: 'Image Type',
|
||||
file_name: 'File Name',
|
||||
|
||||
// Image to Base64
|
||||
image_input_title: 'Input Image',
|
||||
base64_output_title: 'Base64 Output',
|
||||
select_image: 'Select Image File',
|
||||
choose_file: 'Choose File',
|
||||
uploaded_image: 'Uploaded Image',
|
||||
no_uploaded_image: 'No image uploaded yet. Select an image file to convert.',
|
||||
output_placeholder: 'Base64 output will be displayed here...',
|
||||
include_prefix: 'Include data URL prefix (data:image/type;base64,)',
|
||||
copy_base64: 'Copy Base64',
|
||||
save_as_text: 'Save as Text File',
|
||||
invalid_image_file: 'Invalid image file. Please select a valid image.',
|
||||
file_too_large: 'File is too large. Maximum size is 10MB.',
|
||||
file_reading_error: 'Error reading file',
|
||||
conversion_success: 'Image successfully converted to Base64',
|
||||
|
||||
// Tabs and mode switching
|
||||
base64_to_image_tab: 'Base64 to Image',
|
||||
image_to_base64_tab: 'Image to Base64',
|
||||
switch_to_image_to_base64: 'Switch to Image to Base64',
|
||||
switch_to_base64_to_image: 'Switch to Base64 to Image',
|
||||
|
||||
// Guides
|
||||
usage_guide: 'Usage Guide',
|
||||
guide_1: 'Paste a Base64 encoded string to convert it to an image',
|
||||
guide_2: 'Supports both pure Base64 and data URL formats (data:image/png;base64,...)',
|
||||
guide_3: 'The tool will attempt to detect the image format automatically',
|
||||
guide_4: 'You can download the converted image with your preferred file name',
|
||||
guide_5: 'Upload an image file to convert it to Base64 string',
|
||||
guide_6: 'Choose whether to include the data URL prefix or only the raw Base64 data',
|
||||
guide_7: 'The generated Base64 can be copied to clipboard or saved as a text file',
|
||||
guide_8: 'Supported image formats include JPG, PNG, GIF, BMP, WebP, and SVG'
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import { base64ToImageEn } from './en';
|
||||
import { base64ToImageZh } from './zh';
|
||||
|
||||
const base64ToImage = {
|
||||
en: base64ToImageEn,
|
||||
zh: base64ToImageZh
|
||||
};
|
||||
|
||||
export default base64ToImage;
|
||||
@@ -0,0 +1,55 @@
|
||||
export const base64ToImageZh = {
|
||||
title: 'Base64图片转换器',
|
||||
description: 'Base64字符串与图片互相转换',
|
||||
|
||||
// Base64转图片
|
||||
input_title: '输入Base64字符串',
|
||||
output_title: '图片输出',
|
||||
base64_input: 'Base64字符串',
|
||||
input_placeholder: '在此粘贴您的Base64编码图片字符串...',
|
||||
load_example: '加载示例',
|
||||
clear_all: '清空',
|
||||
copy: '复制Base64',
|
||||
download_image: '下载图片',
|
||||
converted_image: '转换后的图片',
|
||||
no_image: '暂无图片显示。粘贴Base64字符串进行转换。',
|
||||
processing_error: '处理Base64字符串时出错',
|
||||
invalid_data_url: '无效的数据URL格式',
|
||||
invalid_base64: '无效的Base64编码字符串',
|
||||
copy_failed: '复制到剪贴板失败',
|
||||
image_type: '图片类型',
|
||||
file_name: '文件名',
|
||||
|
||||
// 图片转Base64
|
||||
image_input_title: '输入图片',
|
||||
base64_output_title: 'Base64输出',
|
||||
select_image: '选择图片文件',
|
||||
choose_file: '选择文件',
|
||||
uploaded_image: '已上传图片',
|
||||
no_uploaded_image: '暂无已上传图片。选择图片文件进行转换。',
|
||||
output_placeholder: 'Base64输出将显示在这里...',
|
||||
include_prefix: '包含数据URL前缀(data:image/type;base64,)',
|
||||
copy_base64: '复制Base64',
|
||||
save_as_text: '保存为文本文件',
|
||||
invalid_image_file: '无效的图片文件。请选择有效的图片。',
|
||||
file_too_large: '文件太大。最大大小为10MB。',
|
||||
file_reading_error: '读取文件出错',
|
||||
conversion_success: '图片已成功转换为Base64',
|
||||
|
||||
// 选项卡和模式切换
|
||||
base64_to_image_tab: 'Base64转图片',
|
||||
image_to_base64_tab: '图片转Base64',
|
||||
switch_to_image_to_base64: '切换到图片转Base64',
|
||||
switch_to_base64_to_image: '切换到Base64转图片',
|
||||
|
||||
// 使用指南
|
||||
usage_guide: '使用指南',
|
||||
guide_1: '粘贴Base64编码字符串将其转换为图片',
|
||||
guide_2: '支持纯Base64和数据URL格式(data:image/png;base64,...)',
|
||||
guide_3: '工具会自动尝试检测图片格式',
|
||||
guide_4: '可以使用您喜欢的文件名下载转换后的图片',
|
||||
guide_5: '上传图片文件将其转换为Base64字符串',
|
||||
guide_6: '选择是否包含数据URL前缀或仅保留原始Base64数据',
|
||||
guide_7: '生成的Base64可以复制到剪贴板或保存为文本文件',
|
||||
guide_8: '支持的图片格式包括JPG、PNG、GIF、BMP、WebP和SVG'
|
||||
};
|
||||
@@ -22,6 +22,7 @@ import unicodeConverter from './unicode_converter';
|
||||
import urlEncoder from './url_encoder';
|
||||
import numberBaseConverter from './number_base_converter';
|
||||
import ymlPropertiesConverter from './yml_properties_converter';
|
||||
import base64ToImage from './base64_to_image';
|
||||
|
||||
export const tools = {
|
||||
json_formatter: jsonFormatter,
|
||||
@@ -47,7 +48,8 @@ export const tools = {
|
||||
unicode_converter: unicodeConverter,
|
||||
url_encoder: urlEncoder,
|
||||
number_base_converter: numberBaseConverter,
|
||||
yml_properties_converter: ymlPropertiesConverter
|
||||
yml_properties_converter: ymlPropertiesConverter,
|
||||
base64_to_image: base64ToImage
|
||||
};
|
||||
|
||||
export default tools;
|
||||
|
||||
@@ -27,7 +27,8 @@ export const zh = {
|
||||
unicode_converter: tools.unicode_converter.zh,
|
||||
url_encoder: tools.url_encoder.zh,
|
||||
number_base_converter: tools.number_base_converter.zh,
|
||||
yml_properties_converter: tools.yml_properties_converter.zh
|
||||
yml_properties_converter: tools.yml_properties_converter.zh,
|
||||
base64_to_image: tools.base64_to_image.zh
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -153,6 +153,12 @@ const tools: Tool[] = [
|
||||
icon: faFileAlt,
|
||||
category: ['code'],
|
||||
keywords: ['yml', 'yaml', 'properties', 'yml转properties', 'properties转yml', '配置文件', '转换工具', 'yml互转properties', 'yaml互转properties', '配置转换', 'peizhi', 'pz', 'zhuanhuan', 'zh']
|
||||
},
|
||||
{
|
||||
code: 'base64_to_image',
|
||||
icon: faImage,
|
||||
category: ['image', 'encoding'],
|
||||
keywords: ['base64', 'base64转图片', '图片转base64', '图片', '解码', '编码', '转换', 'base64 to image', 'image to base64', 'image decoder', 'image encoder', 'tupian', 'tp', 'jm', 'bm', 'zhuanhuan', 'zh', 'base64转换']
|
||||
}
|
||||
] as Tool[];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user