ReactJS:上传前预览多张图片

IT技术 javascript reactjs
2021-05-22 16:33:43

我试图在使用 ReactJS 和FileReader()API上传到服务器之前在浏览器中预览多个图像但是,我遇到的问题是每次选择一些图像进行预览时,只显示最后一张图像。

我的代码如下所示:

class App extends Component {
    constructor(props){
        super(props);
        this.state = {
            id: "upload-photo",
            imageURI: null
        }
    }

    buildImgTag(){
        let imgTag = null;
        if (this.state.imageURI !== null) {
            imgTag = (
                <div className="photo-container">
                    <img className="photo-uploaded" src={this.state.imageURI} alt="Photo uploaded"/>
                </div>
            );
            return imgTag;
        }
    }

    readURI(e){
        if (e.target.files) {
            let filesAmount = e.target.files.length;
            let i;
            for (i = 0; i < filesAmount; i++) {
                let reader = new FileReader();
                reader.onload = function(ev) {
                    this.setState (
                        {
                            imageURI: ev.target.result
                        }
                    )
                }.bind(this);
                reader.readAsDataURL(e.target.files[i]);
            }
        }
    }

    handleChange(e){
        this.readURI(e);
        if (this.props.onChange !== undefined) {
            this.props.onChange(e);
        }
    }

    render() {
        const imgTag = this.buildImgTag();

        return (
            <div className="container">
                <div className="row justify-content-center">

                    <div className="col-md-6">
                        <div className="card">
                            <div className="card-header" style={{backgroundColor: 'rgb(232, 245, 253)', borderTopLeftRadius: '4px', borderTopRightRadius: '4px', display: 'flex', maxHeight: '50vh', minHeight: '25vh', overflow: 'hidden'}}>
                                <div className="avatar">
                                    <img src="http://laratweet.local:8080/images/avatar-default.png" alt="User Avatar" className="user-avatar"/>
                                </div>
                                <div id="textEditor">
                                    <form method="post" action="" encType="multipart/form-data">
                                        <textarea name="" id="richTextArea" placeholder="What's happening?"></textarea>
                                        {imgTag}
                                        <div id="theRibbon">
                                            <div>
                                                <input
                                                    id={this.state.id}
                                                    type="file"
                                                    name=""
                                                    accept="image/gif,image/jpeg,image/jpg,image/png,video/mp4,video/x-m4v"
                                                    title="Add photos or video"
                                                    onChange={this.handleChange.bind(this)}
                                                    multiple
                                                />
                                                <label htmlFor={this.state.id}>
                                                    <figure>
                                                        <svg xmlns="http://www.w3.org/2000/svg" width="30" height="27"
                                                             viewBox="0 0 20 17" className="upload-icon">
                                                            <path
                                                                d="M10 0l-5.2 4.9h3.3v5.1h3.8v-5.1h3.3l-5.2-4.9zm9.3 11.5l-3.2-2.1h-2l3.4 2.6h-3.5c-.1 0-.2.1-.2.1l-.8 2.3h-6l-.8-2.2c-.1-.1-.1-.2-.2-.2h-3.6l3.4-2.6h-2l-3.2 2.1c-.4.3-.7 1-.6 1.5l.6 3.1c.1.5.7.9 1.2.9h16.3c.6 0 1.1-.4 1.3-.9l.6-3.1c.1-.5-.2-1.2-.7-1.5z"/>
                                                        </svg>
                                                    </figure>
                                                    <span className="tooltiptext">Add photos or video</span>
                                                </label>
                                            </div>
                                            <button type="submit" className="tweet">Tweet</button>
                                        </div>
                                    </form>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

我希望在浏览器中显示/预览所有选定的图像,但只预览最后一个图像。我究竟做错了什么?

3个回答

这里的主要问题是您的App组件状态目前只能跟踪一个imageURI. 考虑修改state模型,以便imageURI可以存储和呈现 's数组

this.state = {
    id: "upload-photo",
    imageArray: [] /* Replace imageURI with an array for multiple images */
}

接下来,您需要更新readURI()以在组件状态下存储多个图像。一种方法是使用Promise.all()异步加载一组图像:

readURI(e){
    if (e.target.files) {

        /* Get files in array form */
        const files = Array.from(e.target.files);

        /* Map each file to a promise that resolves to an array of image URI's */ 
        Promise.all(files.map(file => {
            return (new Promise((resolve,reject) => {
                const reader = new FileReader();
                reader.addEventListener('load', (ev) => {
                    resolve(ev.target.result);
                });
                reader.addEventListener('error', reject);
                reader.readAsDataURL(file);
            }));
        }))
        .then(images => {

            /* Once all promises are resolved, update state with image URI array */
            this.setState({ imageArray : images })

        }, error => {        
            console.error(error);
        });
    }
}

最后,您只需要更新buildImgTag()以便呈现多个图像。一种方法可能是:

buildImgTag(){

    return <div className="photo-container">
    { 
      this.state.imageArray.map(imageURI => 
      (<img className="photo-uploaded" src={imageURI} alt="Photo uploaded"/>)) 
    }
    </div>
}

此外,这里是一个 jsFiddle,显示了文件读取逻辑的运行情况。希望有帮助

对于那些寻求相同答案的人,有一个更简洁的选择,它不涉及除 javascript 之外的任何其他内容:

HTML:

<!DOCTYPE html>
<html>
    <head>
        <title>File API - FileReader as Data URL</title>
    </head>
    <body>
        <header>
            <h1>File API - FileReader</h1>
        </header>
        <article>
            <label for="files">Select multiple files: </label>
            <input id="files" type="file" multiple/>
            <output id="result" />
        </article>
    </body>
</html>

CSS:

body
{
    font-family: 'Segoe UI';
    font-size: 12pt;
}

header h1
{
    font-size:12pt;
    color: #fff;
    background-color: #1BA1E2;
    padding: 20px;
}
article
{
    width: 80%;
    margin:auto;
    margin-top:10px;
}
.thumbnail
{
    height: 100px;
    margin: 10px;    
}

Java脚本

window.onload = function()
{

    //Check File API support
    if ( window.File && window.FileList && window.FileReader )
    {
        var filesInput = document.getElementById("files");

        filesInput.addEventListener 
        ( 
            "change", function ( event )
            {
                var files = event.target.files; //FileList object
                var output = document.getElementById ( "result" );

                for ( var i = 0; i< files.length; i++ )
                {
                    var file = files [ i ];

                    //Only pics
                    if ( !file.type.match ( 'image' ) )
                    continue;

                    var picReader = new FileReader();

                    picReader.addEventListener 
                    ( 
                        "load", function ( event )
                        {                    
                            var picFile = event.target;

                            var div = document.createElement ( "div" );

                            div.innerHTML = "<img class='thumbnail' src='" + picFile.result + "'" + "title='" + picFile.name + "'/>";

                            output.insertBefore ( div, null );
                        }
                    );

                   //Read the image
                   picReader.readAsDataURL ( file );
               }                               
           }
        );
    }
    else
    {
        console.log ( "Your browser does not support File API" );
    }
}

http://jsfiddle.net/0GiS0/Yvgc2/

Tentei杜兰特微米直径的待办事项一个resposta做AMIGO @戴克丹尼Ëacabei utilizando一个primeira linha德SUAresolução,ambas作为respostas圣保罗nativas做的JavaScript MAS parece阙Ø的FileReader() DEVE SERVIR对aplicaçõesMAIS complexas科莫POR exemplo salvar作为imagens EM的base64 em um banco de dados, não tenho certeza...

Acabei resolvendo sem 转换器 para Base64 de uma forma muito " mais simples" Primeiro Declarationi meus States

const [files, setFiles] = useState([]);
const [previews, setPreviews] = useState([]);
const [files, setFiles] = useState([]);

...

const handleUpload = (event) => {
const fileList = Array.from(event.target.files);

setFiles(fileList); //Passei o valor de fileList para o State Files

const mappedFiles = fileList.map((file) => ({
  ...file,
  preview: URL.createObjectURL(file),
}));

setPreviews(mappedFiles); };

...Em meu Styled-Component de 上传:

<UploadPhoto type="file" id="files" accept="image/*" multiple="true" onChange={(e) => handleUpload(e)} />

E finalmente para exibição dos arquivos fiz um array.Map

if (previews.length > 0) {
  return (
    <FormGroup> //styled-component (div) para organizar os itens do form
      <UploadContainer> //styled-component (div) display: flex para suportar as imagens
        {previews.map((file) => <ImgUpload preview={file.preview} />)} //styled-component 
        //(div) que se repete de acordo com o número de imagens
      </UploadContainer>
    </FormGroup>
  );
}

A melhor forma para exibição das imagens em minha opinião é colocando como background-image de alguma div juntamente com as propriedades background-repeat: no-repeat, background-size: cover e background-position: 50% 50%

export const ImgUpload = styled.div`
flex-direction: row;
text-align: center;
margin: 10px;
background-image: ${(props) => (props.preview ? `url(${props.preview})` : null)} ;
min-height: 100px;
min-width: 100px;
border-radius: 5px;
background-repeat: no-repeat;
background-size: cover;
background-position: 50% 50%;
margin-right: 5px;
`;