图片上传解决途径

1. 直接上传到后端服务器

  • 方案描述: 将图片文件通过表单或 AJAX 请求直接上传到后端服务器,后端接收到图片后进行存储,并返回图片的 URL。
  • 优点: 实现简单,前后端统一管理资源。
  • 缺点: 如果流量大,后端服务器的存储和带宽压力会增加。
  • 适用场景: 小型项目、没有复杂存储需求的应用。

实现方式:

  • 使用 HTML 表单(<form>)直接提交。
  • 使用 JavaScript 的 FormData 结合 fetchaxios 进行上传。

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);
      }
    });

总结

  • 小型项目、简单需求: 直接上传到后端服务器或使用第三方图床。
  • 大型项目、稳定性要求高: 上传到云存储。
  • 大文件或不稳定网络: 使用分片上传。

先做一个小的总结,有待加强应用与实践。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值