当图像源 URL 更改为 AWS S3 存储桶中图像的 URL 时,浏览器不刷新图像

问题描述 投票:0回答:1

我的 React 应用程序允许用户从他们的系统和 Facebook 上传照片(尚未实现)。从系统上传后,显示的图片的URL就是对象URL。单击“保存”后,系统会向服务器请求每个图像的预签名 URL。预签名 URL 被分割以仅获取 URL 部分(直到 .png 或 .jpg)。然后图像源 URL 更改为预签名 URL。期望通过从新图像源加载图像来刷新图像。但事实并非如此。但是,如果我右键单击选择加载图像,则会加载图像。我尝试了诸如cachebreaker之类的东西,将图像渲染代码放入react组件中,使用onError后备,图像上的唯一键。不起作用。谁能让我知道解决方法。代码如下。

photopicker.js

import Box from '@mui/material/Box'
import * as React from 'react';
import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";
import '../../css/styles.css'
import Typography from '@mui/material/Typography';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faRectangleXmark } from "@fortawesome/free-solid-svg-icons";
import { faTrashCan } from "@fortawesome/free-solid-svg-icons";
import { faComputer } from "@fortawesome/free-solid-svg-icons";
import FacebookIcon from '@mui/icons-material/Facebook';
import Icon from '@mui/material/Icon';
import { useRef } from 'react';
import Button from '@mui/material/Button';
// import { Provider } from 'react-redux';
import { store } from '../appstate/store'
// import { useSelector, useDispatch } from 'react-redux'
// import { picArrayAdd, picArrayDel } from '../appstate/photopickerslice';
import axios from 'axios';
import Tooltip from '@mui/material/Tooltip';

export default function PhotoPicker() {
    const inputRef = useRef();
    const openFileDialog = (e) => {
        inputRef.current.value = null;
        inputRef.current.click()
    }
    // const initialImageArrayState = [];
    const [presignedURL, setPresignedURL] = useState('');
    const [imageArray, setImageArray] = useState([]);
    const [displaySaveCancelButton, setDisplaySaveCancelButton] = useState("none");
    const [profilePic, setProfilePic] = useState('');

    useEffect(() => {
        imageArray.length !== 0 ? setDisplaySaveCancelButton("") : setDisplaySaveCancelButton("none")
    })
  
    const inputFilePickerFunc = (e) => {
        // console.log(e.target.files);
        var file = e.target.files;
        var k = Object.keys(file);
        var objStore = [];
        k.forEach(
            (key) => {
                objStore.push(file[k[key]]);
            }
        );

        //Check if file is image
        let pattern = /image/
        objStore.forEach((index) => {
            let text = index.type;
            /* console.log(text); */
            let result = text.match(pattern);
            if (result == null) {
                console.log('invalid file');
                objStore.splice(index, 1)
            }
        })
        //Check image size
        objStore.forEach((index) => {
            let imageSize = index.size;
            /* console.log(text); */
            if (imageSize > 15728640) {
                console.log('file ' + index + ' image is bigger than 15 MB size limit.');
                objStore.splice(index, 1)
            }
        })

        //check image dimensions

        objStore.forEach(
            (item) => {
                var im = new Image();
                im.src = URL.createObjectURL(item);
                im.onload = function () {
                    if (im.width < 400 || im.height < 500) {
                        objStore.splice(item, 1);
                        console.log('dimensions should be minimum 400 x 500 of image ' + item.name)

                    }
                    else {
                        console.log(imageArray);
                        setImageArray(prevState => {
                            return [...prevState, { "imagesource": im.src, "profilepic": "no", "file": item, "inS3": "no" }]
                        }
                        )

                    }
                }
                console.log(imageArray)
            }
        )

    }
    const setImgProfilePic = (e) => {
               setProfilePic(e.currentTarget.id);
        let imageArrayDump = [...imageArray];
        for (let i = 0; i < imageArrayDump.length; i++) {
            if (imageArrayDump[i].profilepic == "yes") {
                imageArrayDump[i].profilepic = "no";
                break;
            }
        }
        for (let k = 0; k < imageArrayDump.length; k++) {
            if (imageArrayDump[k].imagesource == e.currentTarget.id) {
                imageArrayDump[k].profilepic = "yes";
                setImageArray([...imageArrayDump]);
                break;
            }
        }

    }
    function deleteItem(e, item) {
        e.stopPropagation();
        let indexofItemtoBeDeleted;
        let dupArr = imageArray.slice();
        for (let i = 0; i < dupArr.length; i++) {
            if (dupArr[i].imagesource == item) {
                indexofItemtoBeDeleted = i;
                break;
            }
        }
        dupArr.splice(indexofItemtoBeDeleted, 1);
        setImageArray([...dupArr]);
    }
      
   
    function savePics() {  
        //check in the image array the number of items with inS3 = no, and send this number to API
        let presignedURLsRequired = [];
        for (let i = 0; i < imageArray.length; i++) {
            if (imageArray[i].inS3 == "no") {
                presignedURLsRequired.push(imageArray[i].file.type);
            }
        }
        axios.post('/presignedUploadURL', {
            "numberOfPresignedURLsRequired": presignedURLsRequired
        }).then(
            (res) => {
                let imageArrayDump = [...imageArray];
                for (let i = 0; i < imageArrayDump.length; i++) {
                    for (let k = 0; k < res.data.length; k++) {
                        if (imageArrayDump[i].file.type == res.data[k].imageType) {
                            imageArrayDump[i].presignedURL = res.data[k].presignedURL;
                            res.data.splice(k, 1);
                        }
                    }
                }
                console.log(imageArrayDump);
                for (let j = 0; j < imageArrayDump.length; j++) {
                    axios.put(imageArrayDump[j].presignedURL, imageArrayDump[j].file)
                        .then(
                            (res) => {
                                if (res.status == 200) {
                                    imageArrayDump[j].inS3 = "yes";
                                }
                            }

                        )
                        .catch(res => console.log(res));
                }
                for(let l = 0; l < imageArrayDump.length; l++){
                     let newImageSource = imageArrayDump[l].presignedURL.split('?')[0];
                     imageArrayDump[l].imagesource = newImageSource;
                     delete imageArrayDump[l].presignedURL;
                 }
                            
                setImageArray([...imageArrayDump]);
            }
        ).catch(err => console.log(err));

      
    }
    
    return (

        <Box sx={{ border: 1, display: 'flex', flexWrap: 'wrap', width: '50%', margin: 'auto', minWidth: '570px' }}>
            <Box sx={{ display: 'flex', border: 1, borderColor: 'red', width: '100%', flexWrap: 'nowrap' }}>

                <Typography sx={{ border: 1, width: '100%', minHeight: '30px', textAlign: "center" }}>
                    Upload pictures
                </Typography>

                <Icon onClick={() => alert("hi")} sx={{ border: 1, cursor: "pointer" }}>
                    <FontAwesomeIcon icon={faRectangleXmark} />
                </Icon>
            </Box>
            <Box sx={{ border: 1, width: '49.5%', minHeight: '50px', textAlign: "center", cursor: "pointer" }} onClick={openFileDialog}><input ref={inputRef} type="file" multiple onChange={inputFilePickerFunc} style={{ display: 'none' }}></input><Typography>Computer</Typography><Icon><FontAwesomeIcon icon={faComputer} /></Icon></Box>
            <Box sx={{ border: 1, width: '49.5%', minHeight: '50px', textAlign: "center" }}><Typography>Facebook</Typography><Icon sx={{ color: 'blue' }}>FacebookIcon</Icon></Box>

            {

                imageArray.length !== 0 &&
                (
                    imageArray.map(
                        (item, index) => {
                            let itemImgSrc = item.imagesource
                         
                            return (

                                <Tooltip key={`${itemImgSrc}1`} title={profilePic == item.imagesource ? "" : "Set as profile pic"}>
                                    <Box key={`${itemImgSrc}2`} id={item.imagesource} style={{ border: profilePic == item.imagesource ? "2px solid red" : "0px" }} onClick={setImgProfilePic} sx={{ width: '49.5%', height: "150px", position: 'relative', cursor: "pointer" }}><Icon id="icon" key={`${itemImgSrc}3`} sx={{ position: 'absolute', right: '0px', cursor: "pointer", color: 'red' }} onClick={(e) => { deleteItem(e, item.imagesource) }}><FontAwesomeIcon icon={faTrashCan} /></Icon><img src={item.imagesource} id={itemImgSrc} index={index} key={`${itemImgSrc}4`} style={{ width: '100%', height: '100%', objectFit: 'contain' }}></img></Box>
                                </Tooltip>

                            )

                        }
                    )
                )

            }

            <Box sx={{ width: '100%', textAlign: "center", display: displaySaveCancelButton }}><Button variant="contained" sx={{ m: 2 }} onClick={savePics}>Save</Button></Box>
        </Box>
    )
}
javascript reactjs node.js amazon-s3 pre-signed-url
1个回答
0
投票

为了确保浏览器从新来源重新加载图像,您可以使用 Cache Busting 方法。您需要为此更新您的

savePics
函数。您可以为此附加一个时间戳查询参数。检查下面的代码👇

const savePics = () => {
    const presignedURLsRequired = imageArray
        .filter((img) => img.inS3 === 'no')
        .map((img) => img.file.type);

    axios.post('/presignedUploadURL', {
        numberOfPresignedURLsRequired: presignedURLsRequired,
    }).then((res) => {
        const imageArrayDump = imageArray.map((img) => {
            const presignedURLObj = res.data.find((urlObj) => urlObj.imageType === img.file.type);
            if (presignedURLObj) {
                axios.put(presignedURLObj.presignedURL, img.file).then((uploadRes) => {
                    if (uploadRes.status === 200) {
                        img.inS3 = 'yes';
                        img.imagesource = `${presignedURLObj.presignedURL.split('?')[0]}?${new Date().getTime()}`;
                    }
                }).catch((error) => console.error(error));
            }
            return img;
        });
        setImageArray([...imageArrayDump]);
    }).catch((err) => console.error(err));
};

另一个变化是确保每个图像元素都有一个唯一的关键道具。检查以下代码:👇

{imageArray.length !== 0 && (
    imageArray.map((item, index) => (
        <Tooltip key={`${item.imagesource}_tooltip`} title={profilePic === item.imagesource ? '' : 'Set as profile pic'}>
            <Box
                key={`${item.imagesource}_box`}
                id={item.imagesource}
                style={{ border: profilePic === item.imagesource ? '2px solid red' : '0px' }}
                onClick={setImgProfilePic}
                sx={{ width: '49.5%', height: '150px', position: 'relative', cursor: 'pointer' }}
            >
                <Icon id="icon" key={`${item.imagesource}_icon`} sx={{ position: 'absolute', right: '0px', cursor: 'pointer', color: 'red' }} onClick={(e) => deleteItem(e, item.imagesource)}>
                    <FontAwesomeIcon icon={faTrashCan} />
                </Icon>
                <img src={item.imagesource} id={item.imagesource} index={index} key={`${item.imagesource}_img`} style={{ width: '100%', height: '100%', objectFit: 'contain' }} />
            </Box>
        </Tooltip>
    ))
)}
© www.soinside.com 2019 - 2024. All rights reserved.