我的 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>
)
}
为了确保浏览器从新来源重新加载图像,您可以使用 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>
))
)}