admin管理员组文章数量:1302406
I am trying to preview multiple images in the browser before they are uploaded to the server using ReactJS and the FileReader()
API. The problem that I have However is every time I select some images for preview, only the last image is displayed.
My code looks like this:
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=":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="" 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>
);
}
}
I expect all images selected to be displayed / previewed in the browser but only the last image is previewed. What am I doing wrong?
I am trying to preview multiple images in the browser before they are uploaded to the server using ReactJS and the FileReader()
API. The problem that I have However is every time I select some images for preview, only the last image is displayed.
My code looks like this:
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/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>
);
}
}
I expect all images selected to be displayed / previewed in the browser but only the last image is previewed. What am I doing wrong?
Share edited Apr 5, 2019 at 2:31 Dacre Denny 30.4k5 gold badges51 silver badges66 bronze badges asked Apr 5, 2019 at 0:59 Israel ObanijesuIsrael Obanijesu 6961 gold badge14 silver badges28 bronze badges3 Answers
Reset to default 5The main problem here is that your App
ponents state can currently only track one imageURI
. Consider revising the state
model so that an array of imageURI
's can be stored and rendered:
this.state = {
id: "upload-photo",
imageArray: [] /* Replace imageURI with an array for multiple images */
}
Next, you'll need to update readURI()
so that it stores multiple images in the ponent state. One approach would be to use Promise.all()
to load an array of images asynchronously:
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);
});
}
}
Lastly, you'll just need to update buildImgTag()
so that multiple images are rendered. One approach to that might be:
buildImgTag(){
return <div className="photo-container">
{
this.state.imageArray.map(imageURI =>
(<img className="photo-uploaded" src={imageURI} alt="Photo uploaded"/>))
}
</div>
}
Also, here is a jsFiddle showing the file reading logic in action. Hope that helps
For those ing looking for the same answer, there is a cleaner option which doesn't involve faffing around with anything other than 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;
}
JAVASCRIPT
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/0GiS0/Yvgc2/
Tentei durante um dia todo a resposta do amigo @Dacre Denny e acabei utilizando a primeira linha de sua resolução, ambas as respostas são nativas do JavaScript mas parece que o FileReader() deve servir para aplicações mais plexas o por exemplo salvar as imagens em base64 em um banco de dados, não tenho certeza...
Acabei resolvendo sem converter para Base64 de uma forma muito "mais simples" Primeiro declarei 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 Upload:
<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-ponent (div) para organizar os itens do form
<UploadContainer> //styled-ponent (div) display: flex para suportar as imagens
{previews.map((file) => <ImgUpload preview={file.preview} />)} //styled-ponent
//(div) que se repete de acordo o número de imagens
</UploadContainer>
</FormGroup>
);
}
A melhor forma para exibição das imagens em minha opinião é colocando o background-image de alguma div juntamente 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;
`;
本文标签: javascriptReactJS Preview multiple images before uploadStack Overflow
版权声明:本文标题:javascript - ReactJS: Preview multiple images before upload - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741660815a2391023.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论