1. 直接上传到后端服务器
- 方案描述: 将图片文件通过表单或 AJAX 请求直接上传到后端服务器,后端接收到图片后进行存储,并返回图片的 URL。
- 优点: 实现简单,前后端统一管理资源。
- 缺点: 如果流量大,后端服务器的存储和带宽压力会增加。
- 适用场景: 小型项目、没有复杂存储需求的应用。
实现方式:
- 使用 HTML 表单(
<form>
)直接提交。 - 使用 JavaScript 的
FormData
结合fetch
或axios
进行上传。
2. 将图片上传到云存储(如 AWS S3、阿里云 OSS、腾讯云 COS 等)
- 方案描述: 前端将图片文件上传到云存储,云存储返回图片 URL,或者后端生成预签名 URL,前端使用这个 URL 直接上传图片。
- 优点: 云服务提供商有高可靠的存储、带宽和 CDN 加速,适合大规模应用。
- 缺点: 需要额外配置云存储服务,涉及到费用。
- 适用场景: 大型项目、需要高并发、稳定性和全球访问的场景。
实现方式:
- 前端使用 SDK 或直接与云存储 API 交互。
- 使用formData包装文件上传,返回链接
- 后端生成预签名 URL,前端用该 URL 上传。
- 举一个自己写的react+antd上传到oss的例子
-
<Form.Item label="网站logo" name="logo" rules={[{ required: true, message: "请上传logo" }]} > <Space> <ImgCrop rotationSlider> <Upload fileList={logoFileList} listType="picture-card" onPreview={handlePreview} onChange={handleChange} showUploadList={{ showPreviewIcon: true, showRemoveIcon: true, }} style={{ display: "inline-block" }} maxCount={1} beforeUpload={beforeUploadLogo} className={styles.uploadLogo} > {logoFileList.length >= 1 ? null : uploadButton} </Upload> </ImgCrop> </Space> <Modal centered visible={previewOpen} title={previewTitle} footer={null} onCancel={handleCancel} > <img alt="example" style={{ width: "100%" }} src={previewImage} /> </Modal> </Form.Item> // base64本地预览 const getBase64 = (file: RcFile): Promise<string> => new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result as string); reader.onerror = (error) => reject(error); }); // 上传按钮 const uploadButton = ( <Button icon={<UploadOutlined />} style={{ borderRadius: 4 }}> 上传文件以替换 </Button> ); // 预览控制 const [previewOpen, setPreviewOpen] = useState(false); const [previewImage, setPreviewImage] = useState(""); const [previewTitle, setPreviewTitle] = useState(""); // logo文件列表 const [logoFileList, setLogoFileList] = useState<UploadFile[]>([ ]); // 取消预览 const handleCancel = () => setPreviewOpen(false); // 本地预览函数 const handlePreview = async (file: UploadFile) => { if (!file.url && !file.preview) { file.preview = await getBase64(file.originFileObj as RcFile); } setPreviewImage(file.url || (file.preview as string)); setPreviewOpen(true); setPreviewTitle( file.name || file.url!.substring(file.url!.lastIndexOf("/") + 1), ); }; // 上传前数据处理 const beforeUploadLogo = async (file:any) => { // 使用 FormData 封装文件 const formData = new FormData(); formData.append("file", file); // 发起自定义上传请求 const res = (await FileUpload(formData)) as any; if (res) { setLogoFileList([ { uid: "-1", name: "logo", status: "done", url: res, }, ]); editForm.setFieldsValue({ logo: res }); } else { // 清空上传列表,提示上传失败 setLogoFileList([{ uid: "-1", name: "logo", status: "error", }]); } // 返回 false 以阻止默认的上传行为 return false; }; // logo文件改变 const handleChange: UploadProps["onChange"] = ({ fileList: newFileList }) => { // 如果上传失败或取消当前图片,清空表单项 if (!newFileList.length) { setLogoFileList(newFileList) editForm.setFieldsValue({ logo: "" }); } };
3. 将图片上传到第三方图床(如 Imgur、SM.MS 等)
- 方案描述: 前端或后端通过 API 将图片上传到第三方图床,并获取图片链接。
- 优点: 实现简单,不需要自己维护存储和带宽。
- 缺点: 第三方图床可能不稳定、限制上传数量、或服务关闭风险。
- 适用场景: 小型项目、临时性或个人应用。
4. 使用 Base64 或 Blob 格式上传图片
- 方案描述: 将图片转为 Base64 或 Blob 字符串,作为请求体的一部分传给后端,后端再解析和存储。
- 优点: 适合小型图片数据传输,不需要处理文件上传逻辑。
- 缺点: 图片大时会导致请求体过大,传输效率低下。
- 适用场景: 只需在小范围或内部系统传递小图片数据。
- 以下给俩个生成的实例
-
async function uploadImageBase64(base64Image: string) { try { const response = await fetch('/api/upload', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ image: base64Image }), }); const data = await response.json(); console.log('Upload success:', data); } catch (error) { console.error('Upload failed:', error); } } // 将图片文件转换为 Base64 格式 function convertToBase64(file: File): Promise<string> { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result as string); reader.onerror = error => reject(error); }); } // 使用示例 const fileInput = document.querySelector('input[type="file"]'); fileInput?.addEventListener('change', async (event) => { const file = (event.target as HTMLInputElement).files?.[0]; if (file) { const base64Image = await convertToBase64(file); uploadImageBase64(base64Image); } });
async function uploadImageBlob(imageBlob: Blob) { const formData = new FormData(); formData.append('image', imageBlob, 'image.png'); // 第三个参数是文件名,可以根据需要设置 try { const response = await fetch('/api/upload', { method: 'POST', body: formData, // 上传 Blob 时使用 FormData }); const data = await response.json(); console.log('Upload success:', data); } catch (error) { console.error('Upload failed:', error); } } // 使用示例 const fileInput = document.querySelector('input[type="file"]'); fileInput?.addEventListener('change', (event) => { const file = (event.target as HTMLInputElement).files?.[0]; if (file) { uploadImageBlob(file); // 直接上传 Blob 格式 } });
5. 分片上传(断点续传)
- 方案描述: 将大图片或文件分片上传,每个分片单独处理和传输,支持断点续传和上传失败重试。
- 优点: 适合大文件上传,可以减轻服务器压力,提升上传可靠性。
- 缺点: 实现复杂,需要在前后端配合实现分片和合并。
- 适用场景: 需要上传大文件或不稳定网络环境下的文件上传。
- 以下生成一个断点续传的例子
-
async function uploadChunk(file: File, chunk: Blob, chunkIndex: number, totalChunks: number) { const formData = new FormData(); formData.append('file', chunk); formData.append('fileName', file.name); formData.append('chunkIndex', chunkIndex.toString()); formData.append('totalChunks', totalChunks.toString()); return fetch('/api/upload-chunk', { method: 'POST', body: formData, }); } async function uploadFile(file: File) { const chunkSize = 2 * 1024 * 1024; // 每个分片大小:2MB const totalChunks = Math.ceil(file.size / chunkSize); for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) { const start = chunkIndex * chunkSize; const end = Math.min(start + chunkSize, file.size); const chunk = file.slice(start, end); // 获取文件的分片 const response = await uploadChunk(file, chunk, chunkIndex, totalChunks); if (!response.ok) { console.error(`Chunk ${chunkIndex + 1} upload failed.`); return; } console.log(`Chunk ${chunkIndex + 1}/${totalChunks} uploaded successfully.`); } console.log('All chunks uploaded successfully.'); } // 选择文件后开始上传 const fileInput = document.querySelector('input[type="file"]'); fileInput?.addEventListener('change', async (event) => { const file = (event.target as HTMLInputElement).files?.[0]; if (file) { await uploadFile(file); } });
总结
- 小型项目、简单需求: 直接上传到后端服务器或使用第三方图床。
- 大型项目、稳定性要求高: 上传到云存储。
- 大文件或不稳定网络: 使用分片上传。
先做一个小的总结,有待加强应用与实践。