React/Django项目中的Cloudinary图片问题
我正在创建一个电商网站,但在使用Cloudinary上传图片时遇到了一些问题。当我通过管理面板上传时没有任何问题,但当我尝试从我的网站上传时,虽然能上传,但我的图片链接却错误地附加到了另一个Cloudinary链接上。
举个例子:当我通过网站上传一张图片,而不是通过管理面板时,图片的alt属性会在我的网站上显示出来,但当我查看控制台时,它试图获取这个链接:
这个链接是正确的图片链接,但它被附加到了“https://res.cloudinary.com/dt4sw7qtl/image/upload/v1/media/”前面。
链接分开来看:
https://res.cloudinary.com/dt4sw7qtl/image/upload/v1/media/https://res.cloudinary.com/dt4sw7qtl/image/upload/v1711814695/wmtt2ry1yycxdukggs3f.jpg
如果我从管理面板上传一张正常的图片,然后继续测试订单,这张图片在整个过程中都能正常显示,直到订单支付页面,但在这个页面上,它也试图以同样的方式获取附加到之前链接的图片。
如果需要我提供更多代码来帮助理解,请告诉我。
在页面底部,我有一个类似于订单页面的屏幕,订单摘要,正常工作,如果有人想比较以找出问题。
这是上传图片的屏幕代码:
import React, { useState, useEffect } from 'react'
import axios from 'axios'
import { Link } from 'react-router-dom'
import { Form, Button } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import Loader from '../components/Loader'
import Message from '../components/Message'
import FormContainer from '../components/FormContainer'
import { listProductDetails, updateProduct } from '../actions/productActions'
import { PRODUCT_UPDATE_RESET } from '../constants/productConstants'
function ProductEditScreen({ match, history }) {
const productId = match.params.id
const [name, setName] = useState('')
const [price, setPrice] = useState('0')
const [image, setImage] = useState('')
const [brand, setBrand] = useState('')
const [category, setCategory] = useState('')
const [countInStock, setCountInStock] = useState('0')
const [description, setDescription] = useState('')
const [uploading, setUploading] = useState(false)
const dispatch = useDispatch()
const productDetails = useSelector(state => state.productDetails)
const { error, loading, product } = productDetails
const productUpdate = useSelector(state => state.productUpdate)
const { error: errorUpdate, loading: loadingUpdate, success: successUpdate } = productUpdate
useEffect(() => {
if (successUpdate) {
dispatch({ type: PRODUCT_UPDATE_RESET })
history.push('/admin/productlist')
} else {
if (!product.name || product._id !== Number(productId)) {
dispatch(listProductDetails(productId))
} else {
setName(product.name)
setPrice(product.price)
setImage(product.image)
setBrand(product.brand)
setCategory(product.category)
setCountInStock(product.countInStock)
setDescription(product.description)
}
}
}, [dispatch, product, productId, history, successUpdate])
const submitHandler = (e) => {
e.preventDefault()
dispatch(updateProduct({
_id: productId,
name,
price,
image,
brand,
category,
countInStock,
description
}))
}
const uploadFileHandler = async (e) => {
const file = e.target.files[0]
const formData = new FormData()
formData.append('product_id', productId)
formData.append('image', file)
setUploading(true)
try {
const config = {
headers: {
'Content-Type': 'multipart/form-data'
}
}
const { data } = await axios.post('MyUrlRemovedForQuestion', formData, config)
setImage(data.image_url)
setUploading(false)
} catch (error) {
setUploading(false)
}
}
return (
<div>
<Link to='/admin/productlist'>
Go Back
</Link>
<FormContainer>
<h1>Edit Product</h1>
{loadingUpdate && <Loader />}
{errorUpdate && <Message variant='danger'>{errorUpdate}</Message>}
{loading ? <Loader /> : error ? <Message variant='danger'>{error}</Message>
: (
<Form onSubmit={submitHandler}>
<Form.Group controlId='name'>
<Form.Label>Name</Form.Label>
<Form.Control
type='name'
placeholder='Enter name'
value={name}
onChange={(e) => setName(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group controlId='price'>
<Form.Label>Price</Form.Label>
<Form.Control
type='number'
placeholder='Enter price'
value={price}
onChange={(e) => setPrice(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group controlId='image'>
<Form.Label>Image</Form.Label>
<Form.Control
type='image'
placeholder='Enter image'
value={image}
onChange={(e) => setImage(e.target.value)}
>
</Form.Control>
<Form.Control
type='file'
custom
onChange={uploadFileHandler}
>
</Form.Control>
{uploading && <Loader />}
</Form.Group>
<Form.Group controlId='brand'>
<Form.Label>Brand</Form.Label>
<Form.Control
type='text'
placeholder='Enter brand'
value={brand}
onChange={(e) => setBrand(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group controlId='countInStock'>
<Form.Label>Stock</Form.Label>
<Form.Control
type='number'
placeholder='Enter stock'
value={countInStock}
onChange={(e) => setCountInStock(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group controlId='category'>
<Form.Label>Category</Form.Label>
<Form.Control
type='text'
placeholder='Enter category'
value={category}
onChange={(e) => setCategory(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group controlId='description'>
<Form.Label>Description</Form.Label>
<Form.Control
type='text'
placeholder='Enter description'
value={description}
onChange={(e) => setDescription(e.target.value)}
>
</Form.Control>
</Form.Group>
<Button type='submit' variant='primary'>
Update
</Button>
</Form>
)}
</FormContainer >
</div>
)
}
export default ProductEditScreen
这是订单摘要屏幕的代码:
import React, { useState, useEffect } from 'react'
import { Button, Row, Col, ListGroup, Image, Card, ListGroupItem } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import { PayPalButton } from 'react-paypal-button-v2'
import { Link } from 'react-router-dom'
import Message from '../components/Message'
import Loader from '../components/Loader'
import { getOrderDetails, payOrder } from '../actions/orderActions'
import { ORDER_PAY_RESET } from '../constants/orderConstants'
function OrderScreen({ match }) {
const orderId = match.params.id
const dispatch = useDispatch()
const [SdkReady, setSdkReady] = useState(false)
const orderDetails = useSelector(state => state.orderDetails)
const { order, error, loading } = orderDetails
const orderPay = useSelector(state => state.orderPay)
const { loading: loadingPay, success: successPay } = orderPay
if (!loading && !error) {
order.itemsPrice = order.orderItems.reduce((acc, item) => acc + item.price * item.qty, 0).toFixed(2)
}
const addPayPalScript = () => {
const script = document.createElement('script')
script.type = 'text/javascript'
script.src = ''
script.async = true
script.onload = () => {
setSdkReady(true)
}
document.body.appendChild(script)
}
useEffect(() => {
if (!order || successPay || order._id !== Number(orderId)) {
dispatch({ type: ORDER_PAY_RESET })
dispatch(getOrderDetails(orderId))
} else if (!order.isPaid) {
if (!window.paypal) {
addPayPalScript()
} else {
setSdkReady(true)
}
}
}, [dispatch, order, orderId, successPay])
const successPaymentHandler = (paymentResult) => {
dispatch(payOrder(orderId, paymentResult))
}
return loading ? (
<Loader />
) : error ? (
<Message variant='danger'>{error}</Message>
) : (
<div>
<h1>Order: {order._id}</h1>
<Row>
<Col md={8}>
<ListGroup variant='flush'>
<ListGroup.Item>
<h2>Shipping</h2>
<p><strong>Name: </strong> {order.user.name}</p>
<p><strong>Email: </strong> <a href={`mailto:${order.user.email}`}>{order.user.email}</a></p>
<p>
<strong>To: </strong>
{order.shippingAddress.address}, {order.shippingAddress.city}
{' '}
{order.shippingAddress.postalCode},
{' '}
{order.shippingAddress.country}
</p>
{order.isDelivered ? (
<Message variant='success'>Delivered on {order.deliveredAt}</Message>
) : (
<Message variant='warning'>Not delivered{order.deliveredAt}</Message>
)}
</ListGroup.Item>
<ListGroup.Item>
<h2>Payment Method</h2>
<p>
<strong>Method:</strong>
{order.paymentMethod}
</p>
{order.isPaid ? (
<Message variant='success'>Paid on {order.paidAt}</Message>
) : (
<Message variant='warning'>Not paid{order.paidAt}</Message>
)}
</ListGroup.Item>
<ListGroup.Item>
<h2>Order Items</h2>
{order.orderItems.length === 0 ? <Message variant='info'>
Order is empty
</Message> : (
<ListGroup variant='flush'>
{order.orderItems.map((item, index, product) => (
<ListGroup.Item key={index}>
<Row>
<Col md={1}>
<Image src={item.image} alt={item.name} fluid rounded />
</Col>
<Col>
<Link to={`/product/${item.product}`}>{item.name}</Link>
</Col>
<Col md={4}>
{item.qty} X ${item.price} = ${(item.qty * item.price).toFixed(2)}
</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
)}
</ListGroup.Item>
</ListGroup>
</Col>
<Col md={4}>
<Card>
<ListGroup variant='flush'>
<ListGroup.Item>
<h2>Order Summary</h2>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Items:</Col>
<Col>${order.itemsPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Shipping:</Col>
<Col>${order.shippingPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Tax:</Col>
<Col>${order.taxPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Total:</Col>
<Col>${order.totalPrice}</Col>
</Row>
</ListGroup.Item>
{!order.isPaid && (
<ListGroup.Item>
{loadingPay && <Loader />}
{!SdkReady ? (
<Loader />
) : (
<PayPalButton
amount={order.totalPrice}
onSuccess={successPaymentHandler}
/>
)}
</ListGroup.Item>
)}
</ListGroup>
</Card>
</Col>
</Row>
</div>
)
}
export default OrderScreen
正常工作的屏幕,类似于订单屏幕
import React, { useState, useEffect } from 'react'
import { Button, Row, Col, ListGroup, Image, Card, ListGroupItem } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import { Link } from 'react-router-dom'
import CheckoutSteps from '../components/CheckoutSteps'
import Message from '../components/Message'
import { createOrder } from '../actions/orderActions'
import { ORDER_CREATE_RESET } from '../constants/orderConstants'
function PlaceOrderScreen({ history }) {
const orderCreate = useSelector(state => state.orderCreate)
const { order, error, success } = orderCreate
const dispatch = useDispatch()
const cart = useSelector(state => state.cart)
cart.itemsPrice = cart.cartItems.reduce((acc, item) => acc + item.price * item.qty, 0).toFixed(2)
cart.shippingPrice = (cart.itemsPrice > 100 ? 0 : 10).toFixed(2)
cart.taxPrice = Number((0.082) * cart.itemsPrice).toFixed(2)
cart.totalPrice = (Number(cart.itemsPrice) + Number(cart.shippingPrice) + Number(cart.taxPrice)).toFixed(2)
if (!cart.paymentMethod) {
history.push('/payment')
}
useEffect(() => {
if (success) {
history.push(`/order/${order._id}`)
dispatch({ type: ORDER_CREATE_RESET })
}
}, [success, history])
const placeOrder = () => {
dispatch(createOrder({
orderItems: cart.cartItems,
shippingAddress: cart.shippingAddress,
paymentMethod: cart.paymentMethod,
itemsPrice: cart.itemsPrice,
shippingPrice: cart.shippingPrice,
taxPrice: cart.taxPrice,
totalPrice: cart.totalPrice,
}))
}
return (
<div>
<CheckoutSteps step1 step2 step3 step4 />
<Row>
<Col md={8}>
<ListGroup variant='flush'>
<ListGroup.Item>
<h2>Shipping</h2>
<p>
<strong>To: </strong>
{cart.shippingAddress.address}, {cart.shippingAddress.city}
{' '}
{cart.shippingAddress.postalCode},
{' '}
{cart.shippingAddress.country}
</p>
</ListGroup.Item>
<ListGroup.Item>
<h2>Payment Method</h2>
<p>
<strong>Method:</strong>
{cart.paymentMethod}
</p>
</ListGroup.Item>
<ListGroup.Item>
<h2>Order Items</h2>
{cart.cartItems.length === 0 ? <Message variant='info'>
Your cart is empty
</Message> : (
<ListGroup variant='flush'>
{cart.cartItems.map((item, index) => (
<ListGroup.Item key={index}>
<Row>
<Col md={1}>
<Image src={item.image} alt={item.name} fluid rounded />
</Col>
<Col>
<Link to={`/product/${item.product}`}>{item.name}</Link>
</Col>
<Col md={4}>
{item.qty} X ${item.price} = ${(item.qty * item.price).toFixed(2)}
</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
)}
</ListGroup.Item>
</ListGroup>
</Col>
<Col md={4}>
<Card>
<ListGroup variant='flush'>
<ListGroup.Item>
<h2>Order Summary</h2>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Items:</Col>
<Col>${cart.itemsPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Shipping:</Col>
<Col>${cart.shippingPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Tax:</Col>
<Col>${cart.taxPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Total:</Col>
<Col>${cart.totalPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
{error && <Message variant='danger'>{error}</Message>}
</ListGroup.Item>
<ListGroup.Item>
<div className='d-grid gap-2'>
<Button
type='button'
disabled={cart.cartItems === 0}
onClick={placeOrder}
>Place Order</Button>
</div>
</ListGroup.Item>
</ListGroup>
</Card>
</Col>
</Row>
</div>
)
}
export default PlaceOrderScreen
如果这个问题的表述不清楚,或者我需要添加其他内容,请告诉我。非常感谢!
我尝试从Cloudinary加载图片,但它们被附加到了另一个Cloudinary链接上。
0 个回答
暂无回答