【问题标题】:react open file browser on click a div在单击 div 时对打开的文件浏览器做出反应
【发布时间】:2016-05-26 09:43:57
【问题描述】:

我的反应组件:

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 时,我想打开一个 &lt;input&gt; 文件,其中显示了选择照片的选项。选择照片后,我想获得选择哪张照片的值。我怎么能在反应中做到这一点??

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    使用 React Hooks

    上传新答案

    首先创建你的 Input ref 钩子

    const inputFile = useRef(null) 
    

    然后将其设置为您的 INPUT 并添加一个显示样式:none,因为输入不会显示在屏幕上

    <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>
    

    API for HTML INPUT FILE

    【讨论】:

    • 对于带有反应的打字稿,它应该是 const inputFile = useRef(空);
    【解决方案2】:

    除了将输入放在您的视图上之外,您还需要处理输入内容的更改。为此,请实现 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));
    

    【讨论】:

      【解决方案3】:

      将 ref 属性添加到您的输入中:

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

      改变handleClick函数:

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

      由于您使用的是 ES6,因此您需要将 this 绑定到您的 handleClick 函数,我们可以在构造函数中这样做:

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

      【讨论】:

      • 这只是为了打开对话框。
      【解决方案4】:

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

      打字稿示例:

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

      【讨论】:

      • 我不会在答案中添加 Typescript,因为人们会遇到RefObject 等问题...
      【解决方案5】:

      所有建议的答案都很棒。我超越并允许用户添加图像并立即预览。我使用了 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);
      }
      
      

      【讨论】:

        【解决方案6】:

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

            <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>
        

        【讨论】:

          【解决方案7】:
          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 对我有用的唯一代码

          【讨论】:

          • 你应该在这行后面加一个分号:const { current } = inputRef
          【解决方案8】:

          我最近想用 Material UI 实现一个类似的功能,这种方法类似于 @Niyongabo 实现,除了我使用的是 Material UI 框架并利用 Avatar/Badge 组件。

          我还在使用它之前调整了图像的大小。

          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;
          

          【讨论】:

            【解决方案9】:

            如果您可以使用钩子,这个包将解决您的问题,而无需创建输入元素。这个包不呈现任何 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

            我创建了这个包来解决同样的问题。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2016-08-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-03-08
              • 2020-04-08
              • 1970-01-01
              相关资源
              最近更新 更多