Reactjs 应用程序不在 safari 浏览器中显示图像预览

Reactjs app doesn't show image preview in safari

我正在尝试制作一个用户可以上传图像并将其发送到电子邮件的应用程序,它在除 Safari 之外的所有浏览器上都运行良好。对于移动和网络浏览器,当我选择要上传的图像时,似乎没有任何内容被预览,甚至没有加载(准备发送)。我能做些什么来解决这个问题吗?我的代码目前非常简单:

const EnterDetailPage = props => {
  const [imageUrl, setImageUrl] = useState("");
  const [imageFile, setImageFile] = useState();
  const [upload, setUpload] = useState(null);
const handleUploadChange = async e => {
    setLoading(true);
    const file = e.target.files[0];
    if (!file) {
      return;
    }
    setUpload(URL.createObjectURL(file));
    setImageFile(file);
    const ref = firebase
      .storage()
      .ref()
      .child(uuid.v4());

    const snapshot = await ref.put(file);
    let getImageUrl = await snapshot.ref.getDownloadURL();
    setImageUrl(getImageUrl);
    setLoading(false);
    console.log(getImageUrl);
  };
  let imgPreview = null;
    if (upload) {
    imgPreview = (
      <Avatar
        variant="square"
        src={upload}
        alt="Avatar"
        className={classes.bigAvatar}
      />
    );
  }
  return(
<div className="m-auto p-16 sm:px-24 sm:mx-auto max-w-xl">
        <input
          accept="image/jpeg,image/gif,image/png"
          className="hidden"
          id="button-file"
          type="file"
          // onChange={handleUploadChange}
          onInput={handleUploadChange}
          onClick={event => {
            event.target.value = null;
          }}
        />
        <label
          htmlFor="button-file"
          className={`${classes.bigAvatar} mt-8 bg-gray-300 m-auto flex items-center justify-center relative w-128 h-128 rounded-4 a-mr-16 a-mb-16 overflow-hidden cursor-pointer shadow-1 hover:shadow-xl`}
        >
          <div className="absolute flex items-center justify-center w-full h-full z-50">
          {imageUrl ? null :
            <Icon fontSize="large" color="primary" className="cloud-icon">
              cloud_upload
            </Icon>}
          </div>
          {imgPreview}
        </label>
);
}:

我在这里将我的代码与这篇文章进行了比较:https://w3path.com/react-image-upload-or-file-upload-with-preview/

我似乎做了完全相同的事情...为什么我没有得到相同的结果?

您的 codesandbox 示例有很多内容,但通过将其剥离,我能够找到问题...

Safari 似乎不支持尝试使用 onInput 事件侦听器的 input 元素——永远不会执行回调。相反,您可以使用 onChange 事件侦听器。


对于下面的示例,我通过设置 Promise 和超时来伪造 API 调用,但这不是必需的,仅用于演示目的。此外,我喜欢在多个单独的状态上使用对象,尤其是当状态需要同步时——它也更清晰、更易于阅读,并且功能更像基于 class 的组件。

演示https://jd13t.csb.app/

来源:


components/DetailPage.js

import React, { useRef, useState } from "react";
import { CircularProgress, Icon, Fab } from "@material-ui/core";

const initialState = {
  isLoading: false,
  imageName: "",
  imagePreview: null,
  imageSize: 0
};

const EnterDetailPage = () => {
  const [state, setState] = useState(initialState);
  const uploadInputEl = useRef(null);

  const handleUploadChange = async ({ target: { files } }) => {
    setState(prevState => ({ ...prevState, isLoading: true }));

    const file = files[0];
    await new Promise(res => {
      setTimeout(() => {
        res(
          setState(prevState => ({
            ...prevState,
            imageName: file.name,
            imagePreview: URL.createObjectURL(file),
            imageSize: file.size,
            isLoading: false
          }))
        );
      }, 2000);
    });
  };

  const resetUpload = () => {
    setState(initialState);
    uploadInputEl.current.value = null;
  };

  const uploadImage = async () => {
    if (state.imagePreview)
      setState(prevState => ({ ...prevState, isLoading: true }));

    await new Promise(res => {
      setTimeout(() => {
        res(alert(JSON.stringify(state, null, 4)));
        resetUpload();
      }, 2000);
    });
  };

  const { imagePreview, imageName, imageSize, isLoading } = state;

  return (
    <div style={{ padding: 20 }}>
      <div style={{ textAlign: "center" }}>
        <div>
          <input
            accept="image/jpeg,image/gif,image/png"
            className="hidden"
            id="button-file"
            type="file"
            ref={uploadInputEl}
            onChange={handleUploadChange}
          />
          <label htmlFor="button-file">
            <div>
              {imagePreview ? (
                <>
                  <img
                    src={imagePreview}
                    alt="Avatar"
                    style={{ margin: "0 auto", maxHeight: 150 }}
                  />
                  <p style={{ margin: "10px 0" }}>
                    ({imageName} - {(imageSize / 1024000).toFixed(2)}MB)
                  </p>
                </>
              ) : (
                <Icon fontSize="large" color="primary" className="cloud-icon">
                  cloud_upload
                </Icon>
              )}
            </div>
          </label>
          <Fab
            variant="extended"
            size="large"
            color="primary"
            aria-label="add"
            className=""
            type="button"
            onClick={uploadImage}
          >
            {isLoading ? (
              <CircularProgress style={{ color: "white" }} />
            ) : (
              "Submit"
            )}
          </Fab>
          {imagePreview && (
            <Fab
              variant="extended"
              size="large"
              color="default"
              aria-label="add"
              className=""
              type="button"
              onClick={resetUpload}
            >
              Cancel
            </Fab>
          )}
        </div>
      </div>
    </div>
  );
};

export default EnterDetailPage;