reactjs 点击div时打开文件浏览器

wgxvkvu9  于 2023-01-04  发布在  React
关注(0)|答案(9)|浏览(133)

我的React组件:

import React, { PropTypes, Component } from 'react'

class Content extends Component {
    handleClick(e) {
        console.log("Hellooww world")
    }
    render() {
        return (
            <div className="body-content">
                <div className="add-media" onClick={this.handleClick.bind(this)}>
                    <i className="plus icon"></i>
                    <input type="file" id="file" style={{display: "none"}}/>
                </div>
            </div>
        )
    }
}

export default Content

在这里,当我点击一个带有图标的div时,我想打开一个<input>文件,其中显示了选择照片的选项。选择照片后,我想获得选择照片的值。我如何在react中做到这一点?

qcbq4gxm

qcbq4gxm1#

使用React Hooks上载新答案
首先,创建Input ref钩子。

const inputFile = useRef(null) 
// for using typescript
// const inputFile = useRef(null as (HTMLInputElement | null))
Then set it to your INPUT and add a style to display:none for the input will not show in the screen

<input type='file' id='file' ref={inputFile} style={{display: 'none'}}/>

然后创建您的函数来处理打开的文件,该函数应该位于您正在使用 useRef Hook 的同一个函数内

const onButtonClick = () => {
// `current` points to the mounted file input element
inputFile.current.click();
};

然后将函数设置为Button元素:

<button onClick={onButtonClick}>Open file upload window</button>

HTML输入文件的API

mqkwyuun

mqkwyuun2#

除了把输入放到视图中,你还需要处理输入内容的变化,实现onChange,获取打开的文件信息,如下所示:

<input id="myInput"
   type="file"
   ref={(ref) => this.upload = ref}
   style={{display: 'none'}}
   onChange={this.onChangeFile.bind(this)}
/>

<RaisedButton
    label="Open File"
    primary={false}
    onClick={()=>{this.upload.click()}}
/>

onChangeFile(event) {
    event.stopPropagation();
    event.preventDefault();
    var file = event.target.files[0];
    console.log(file);
    this.setState({file}); /// if you want to upload latter
}

控制台将输出:

File {
  name: "my-super-excel-file.vcs", 
  lastModified: 1503267699000, 
  lastModifiedDate: Sun Aug 20 2017 19:21:39 GMT-0300 (-03), 
  webkitRelativePath: "", 
  size: 54170,
  type:"application/vnd.vcs"
}

现在您可以随心所欲地使用它。但是,如果您要上传它,则必须从以下内容开始:

var form = new FormData();
form.append('file', this.state.file);

YourAjaxLib.doUpload('/yourEndpoint/',form).then(result=> console.log(result));
ogq8wdun

ogq8wdun3#

将ref属性添加到输入中:

<input type="file" id="file" ref="fileUploader" style={{display: "none"}}/>

更改handleClick函数:

handleClick(e) {
    this.refs.fileUploader.click();
}

由于您使用的是ES6,因此需要将其绑定到handleClick函数,我们可以在构造函数中完成此操作:

constructor (props) {
  super(props);
  this.handleClick = this.handleClick.bind(this);
}
4c8rllxm

4c8rllxm4#

React 16.3使用React.createRef()方法提供了更好的方法https://reactjs.org/blog/2018/03/29/react-v-16-3.html#createref-api
typescript 示例:

export class MainMenu extends React.Component<MainMenuProps, {}> {

    private readonly inputOpenFileRef : RefObject<HTMLInputElement>

    constructor() {
        super({})
        this.inputOpenFileRef = React.createRef()
    }

    showOpenFileDlg = () => {
        this.inputOpenFileRef.current.click()
    }

    render() {
        return (
            <div>
                <input ref={this.inputOpenFileRef} type="file" style={{ display: "none" }}/>
                <button onClick={this.showOpenFileDlg}>Open</Button>
            </div>
        )
    }
}
sirbozc5

sirbozc55#

所有建议的答案都很棒。我超越了,允许用户添加图像并立即预览。我使用了React钩子。
谢谢大家的支持
结果应如下所示

import React, { useEffect, useRef, useState } from 'react';

// Specify camera icon to replace button text 
import camera from '../../../assets/images/camera.svg'; // replace it with your path

// Specify your default image
import defaultUser from '../../../assets/images/defaultUser.svg'; // replace it with your path

// Profile upload helper

const HandleImageUpload = () => {
  // we are referencing the file input
  const imageRef = useRef();

  // Specify the default image
  const [defaultUserImage, setDefaultUserImage] = useState(defaultUser);
  
  // On each file selection update the default image
  const [selectedFile, setSelectedFile] = useState();

  // On click on camera icon open the dialog
  const showOpenFileDialog = () => {
    imageRef.current.click();
  };

  // On each change let user have access to a selected file
  const handleChange = (event) => {
    const file = event.target.files[0];
    setSelectedFile(file);
  };

  // Clean up the selection to avoid memory leak
  useEffect(() => {
    if (selectedFile) {
      const objectURL = URL.createObjectURL(selectedFile);
      setDefaultUserImage(objectURL);
      return () => URL.revokeObjectURL(objectURL);
    }
  }, [selectedFile]);

  return {
    imageRef,
    defaultUserImage,
    showOpenFileDialog,
    handleChange,
  };
};

// Image component
export const ItemImage = (props) => {
  const {itemImage, itemImageAlt} = props;
  return (
    <>
      <img
        src={itemImage}
        alt={itemImageAlt}
        className="item-image"
      />
    </>
  );
};

// Button with icon component
export const CommonClickButtonIcon = (props) => {
  const {
    onHandleSubmitForm, iconImageValue, altImg,
  } = props;
  return (
    <div className="common-button">
      <button
        type="button"
        onClick={onHandleSubmitForm}
        className="button-image"
      >
        <img
          src={iconImageValue}
          alt={altImg}
          className="image-button-img"
        />
      </button>
    </div>
  );
};

export const MainProfileForm = () => {
  const {
    defaultUserImage,
    handleChange,
    imageRef,
    showOpenFileDialog,
  } = HandleImageUpload();

  return (
    <div className="edit-profile-container">

      <div className="edit-profile-image">
        <ItemImage
          itemImage={defaultUserImage}
          itemImageAlt="user profile picture"
        />
        <CommonClickButtonIcon // Notice I omitted the text instead used icon
          onHandleSubmitForm={showOpenFileDialog}
          iconImageValue={camera}
          altImg="Upload image icon"
        />
        <input
          ref={imageRef}
          type="file"
          style={{ display: 'none' }}
          accept="image/*"
          onChange={handleChange}
        />
      </div>
    </div>
  );
};

我的CSS

.edit-profile-container {
  position: relative;
}

.edit-profile-container .edit-profile-image {
  position: relative;
  width: 200px;
  display: flex;
  justify-content: center;
}

.edit-profile-container .edit-profile-image .item-image {
  height: 160px;
  width: 160px;
  border-radius: 360px;
}

.edit-profile-container .edit-profile-image .common-button {
  position: absolute;
  right: 0;
  top: 30px;
}

.edit-profile-container .edit-profile-image .common-button .button-image {
  outline: none;
  width: 50px;
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
}

.edit-profile-container .edit-profile-image .common-button .image-button-img {
  height: 30px;
  width: 30px;
  box-shadow: 0 10px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
}
tzxcd3kk

tzxcd3kk6#

您可以将它 Package 在标签中,当您单击标签时,它会单击对话框。

<div>
      <label htmlFor="fileUpload">
        <div>
          <h3>Open</h3>
          <p>Other stuff in here</p>
        </div>
      </label>
      <input hidden id="fileUpload" type="file" accept="video/*" />
    </div>
6qftjkof

6qftjkof7#

import React, { useRef, useState } from 'react'
...
const inputRef = useRef()
....
function chooseFile() {
  const { current } = inputRef
  (current || { click: () => {}}).click()
}
...
<input
   onChange={e => {
     setFile(e.target.files)
    }}
   id="select-file"
   type="file"
   ref={inputRef}
/>
<Button onClick={chooseFile} shadow icon="/upload.svg">
   Choose file
</Button>

我使用next.js

时使用的唯一代码

6tqwzwtp

6tqwzwtp8#

我最近想用材质UI实现一个类似的特性,这种方法类似于@Niyongabo的实现,除了我使用材质UI框架和利用头像/徽章组件。
我也会在使用图像之前调整其大小。

import React, { useEffect, useRef } from "react";
import List from "@material-ui/core/List";
import t from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import { Avatar, Badge } from "@material-ui/core";
import withStyles from "@material-ui/core/styles/withStyles";
import IconButton from "@material-ui/core/IconButton";
import EditIcon from "@material-ui/icons/Edit";
import useTheme from "@material-ui/core/styles/useTheme";

import("screw-filereader");

const useStyles = makeStyles((theme) => ({
  root: {
    display: "flex",
    "& > *": {
      margin: theme.spacing(1)
    }
  },
  form: {
    display: "flex",
    flexDirection: "column",
    margin: "auto",
    width: "fit-content"
  },
  input: {
    fontSize: 15
  },
  large: {
    width: theme.spacing(25),
    height: theme.spacing(25),
    border: `4px solid ${theme.palette.primary.main}`
  }
}));

const EditIconButton = withStyles((theme) => ({
  root: {
    width: 22,
    height: 22,
    padding: 15,
    border: `2px solid ${theme.palette.primary.main}`
  }
}))(IconButton);

export const AvatarPicker = (props) => {
  const [file, setFile] = React.useState("");
  const theme = useTheme();
  const classes = useStyles();

  const imageRef = useRef();

  const { handleChangeImage, avatarImage } = props;

  useEffect(() => {
    if (!file && avatarImage) {
      setFile(URL.createObjectURL(avatarImage));
    }

    return () => {
      if (file) URL.revokeObjectURL(file);
    };
  }, [file, avatarImage]);

  const renderImage = (fileObject) => {
    fileObject.image().then((img) => {
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");
      const maxWidth = 256;
      const maxHeight = 256;

      const ratio = Math.min(maxWidth / img.width, maxHeight / img.height);
      const width = (img.width * ratio + 0.5) | 0;
      const height = (img.height * ratio + 0.5) | 0;

      canvas.width = width;
      canvas.height = height;
      ctx.drawImage(img, 0, 0, width, height);

      canvas.toBlob((blob) => {
        const resizedFile = new File([blob], file.name, fileObject);
        setFile(URL.createObjectURL(resizedFile));
        handleChangeImage(resizedFile);
      });
    });
  };

  const showOpenFileDialog = () => {
    imageRef.current.click();
  };

  const handleChange = (event) => {
    const fileObject = event.target.files[0];
    if (!fileObject) return;
    renderImage(fileObject);
  };

  return (
    <List data-testid={"image-upload"}>
      <div
        style={{
          display: "flex",
          justifyContent: "center",
          margin: "20px 10px"
        }}
      >
        <div className={classes.root}>
          <Badge
            overlap="circle"
            anchorOrigin={{
              vertical: "bottom",
              horizontal: "right"
            }}
            badgeContent={
              <EditIconButton
                onClick={showOpenFileDialog}
                style={{ background: theme.palette.primary.main }}
              >
                <EditIcon />
              </EditIconButton>
            }
          >
            <Avatar alt={"avatar"} src={file} className={classes.large} />
          </Badge>
          <input
            ref={imageRef}
            type="file"
            style={{ display: "none" }}
            accept="image/*"
            onChange={handleChange}
          />
        </div>
      </div>
    </List>
  );
};
AvatarPicker.propTypes = {
  handleChangeImage: t.func.isRequired,
  avatarImage: t.object
};
export default AvatarPicker;

2ledvvac

2ledvvac9#

如果你可以使用钩子,这个包可以解决你的问题,而不需要创建输入元素。这个包不呈现任何html输入元素。你可以简单地在div元素上添加OnClick。
以下是工作演示:https://codesandbox.io/s/admiring-hellman-g7p91?file=/src/App.js

import { useFilePicker } from "use-file-picker";
import React from "react";

export default function App() {
  const [files, errors, openFileSelector] = useFilePicker({
    multiple: true,
    accept: ".ics,.pdf"
  });

  if (errors.length > 0) return <p>Error!</p>;

  return (
    <div>
      <div
        style={{ width: 200, height: 200, background: "red" }}
        onClick={() => openFileSelector()}
      >
        Reopen file selector
      </div>
      <pre>{JSON.stringify(files)}</pre>
    </div>
  );
}

调用openFileSelector()将打开浏览器文件选择器。
文件属性:

lastModified: number;
    name: string;
    content: string;

https://www.npmjs.com/package/use-file-picker
我创建这个包来解决同样的问题。

相关问题