# 目標
解決原本圖片路徑只有純文字的情況:

# 後端
# views
- 要得知是「哪一個產品」需要更新圖片,所以需傳入
product_id用request.FILES.get('image')取得上傳的圖片檔案 - 先不用規範
permission_classes,這樣前端才不用在config額外取token
@api_view(['POST'])
def uploadImage(request):
data = request.data
product_id = data['product_id']
product = Product.objects.get(id=product_id)
product.image = request.FILES.get('image')
product.save()
return Response('圖片上傳成功!')
# urls
path('upload/', views.uploadProductImage,name="product_image_upload"),
# 前端
# 產品編輯前端頁面
- 上傳
Handler函式必須得是async,因為要等待上傳完成後再執行下一步 <Form.File相關寫法已在react-bootstrap V5版本以上被棄用,改用<Form.Control type='file' />
// ProductEditPage.js
const uploadImageHandler = async (e) => {
console.log(e.target.value);
};
return(
...
<Form.Group controlId='image' className='mb-3'>
<Form.Label>產品圖片</Form.Label>
<Form.Control required type='text' placeholder='請輸入產品圖片' value={image} onChange={(e) => setImage(e.target.value)}></Form.Control>
<Form.Control type='file' onChange={uploadImageHandler} />
</Form.Group>
{ImageUploading && <Loader />}
)
逐步慢慢測試,先查看上傳檔案後,主控台是否有印出檔案名稱

確定有擷取到檔案後,用 e.target.files[0]; 取得檔案陣列中的第一個檔案,並宣告一個 FormData 物件,將檔案名稱、產品 ID 一併帶入 FormData 物件傳給後端
// ProductEditPage.js
const uploadImageHandler = async (e) => {
const file = e.target.files[0];
const formData = new FormData();
formData.append("product_image", file); // product_image 後端 uploadProductImage view 接收的參數名稱
formData.append("product_id", productID);
try {
const { data } = await axios.post("/api/products/upload/", formData); // 呼叫後端 uploadProductImage view
setImage(data); // 後端的 return Response 值
setImageUploading(false); // 避免重複點擊
} catch (error) {
setImageUploading(false);
}
};
- 使用
FormData作為請求體時,Axios會自動檢測並設置適當的Content-Type,不需要自己寫config設定,若後端有額外規定permission_classes就需要在config設定token /api/products/upload/的路徑一定要跟後端urls.py設定的一模一樣,原本在upload這少加了/,一直報405 Error
完成後,應就能看到產品成功更換圖片,去專案的 static 資料夾下,也能看到上傳的新圖片檔

# 更改上傳圖片目的地
假設原本上傳圖片的位置是存放到 niceshop/backend/static/images 資料夾下,但經過考量想改將存放用戶上傳圖片的位置改成 niceshop/backend/media/
- STATIC_ROOT(和 STATICFILES_DIRS)主要存放「前端靜態資源」(JS、CSS、圖片)。
- MEDIA_ROOT 主要存放「用戶上傳的檔案」,如:產品圖片、頭像、附件等。
此時需要更改的地方有:
# settings.py
# MEDIA_URL = '/images/' # 改成 -> MEDIA_URL = '/media/'
MEDIA_URL = '/media/' # 圖片的網址,需要去urls.py新增pattern指定此url
...
# MEDIA_ROOT = BASE_DIR / 'static/images' # 改成 -> MEDIA_ROOT = BASE_DIR / 'media/'
MEDIA_ROOT = BASE_DIR / 'media' # 存放用戶上傳的檔案,沒有的話就會直接上傳在根目錄 backend/xxx.png
另外產品圖片預設上傳路徑若是如下:
# models.py
class Product(models.Model):
image = models.ImageField(upload_to='products/', null=True, blank=True,default='products/placeholder.jpg')
...
此時用戶上傳產品圖就會存到 niceshop/backend/media/products/ 資料夾下,能有效區分前端靜態資源與用戶上傳檔案
