提问人:Imperfectcircle 提问时间:10/11/2023 最后编辑:Imperfectcircle 更新时间:10/11/2023 访问量:55
将图像从 React 上传到 Laravel 后端会清空请求正文
Uploading images from React to the Laravel backend empties the request body
问:
我正在使用 Inertia.js 使用 Laravel 和 React 开发一个电子商务网站。我使用 Dropzone 创建了一个用于图像上传的组件。此组件用于表单中的页面,用于创建和更新产品。
关于产品创建,没有问题,一切都按预期进行。当我尝试更新产品时出现问题。如果我只修改字段而不添加图像,则更新可以完美运行。但是,当我添加一个或多个图像时,所有字段都会触发验证错误,就好像它们留空一样。
我尝试在 Request 类中对$request进行“转储和死亡”,并注意到当包含图像时它是空的。我还在前端使用了console.log语句来检查数据是否正确发送并且一切似乎都很好。我认为问题在于我如何发送数据,但我无法确定确切的错误。
这是我的代码
图片上传组件
export default function ImageUploader({ className, setData, data }) {
const [files, setFiles] = useState([]);
const [rejected, setRejected] = useState([]);
useEffect(() => {
setData({ ...data, images: files });
}, [files]);
//[JSON.stringify(...files)]
const onDrop = useCallback((acceptedFiles, rejectedFiles) => {
if (acceptedFiles?.length) {
setFiles((previousFiles) => [
...previousFiles,
...acceptedFiles.map((file) =>
Object.assign(file, { preview: URL.createObjectURL(file) }),
),
]);
}
if (rejectedFiles?.length) {
setRejected((previousFiles) => [
...previousFiles,
...rejectedFiles,
]);
}
}, []);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: {
'image/*': [],
},
maxSize: 1024 * 1000,
});
const removeFile = (name) => {
setFiles((files) => files.filter((file) => file.name !== name));
};
const removeRejected = (name) => {
setRejected((files) => files.filter(({ file }) => file.name !== name));
};
return (
<>
<InputLabel
className={`text-xl`}
htmlFor="images"
value="Immagini"
/>
<p className="pt-2">Dimensioni massime: 1 Mb</p>
<div
{...getRootProps({
className,
})}
>
<input
{...getInputProps()}
id="images"
name="images"
form="productForm"
/>
{isDragActive ? (
<p>Trascina i file qui ...</p>
) : (
<p>Trascina qui i file, o clicca per selezionarli</p>
)}
</div>
....
....
....
}
数据表单
export default function ProductForm({ auth, product, categories }) {
const { data, setData, post, put, processing, errors } = useForm(
product
? {
name: product.name,
description: product.description,
sku: product.sku,
price: product.price,
discounted_price: product.discounted_price,
cost: product.cost,
quantity: product.quantity,
track_quantity: product.track_quantity,
sell_out_of_stock: product.sell_out_of_stock,
status: product.status,
category_id: product.category_id,
images: [],
}
: {
name: '',
description: '',
sku: '',
price: '',
discounted_price: '',
cost: '',
quantity: '',
track_quantity: true,
sell_out_of_stock: false,
status: '',
category_id: '',
images: [],
},
);
const submit = (event) => {
event.preventDefault();
if (product) {
put(route('admin.products.update', product));
return;
}
post(route('admin.products.store'));
};
....
....
....
<ImageUploader
setData={setData}
data={data}
className="mt-2 rounded-lg border border-gray-400 bg-gray-100 p-16 text-center shadow-lg"
/>
请求类
public function authorize(): bool
{
return $this->user()->can('update', $this->route('product'));
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
dd($this->request);
return [
'name' => 'required|string|max:255',
'description' => 'required|string',
'sku' =>
'required|string|unique:products,sku,'.$this->route('product')->id,
'track_quantity' => 'sometimes|nullable|boolean',
'quantity' => 'required_if:track_quantity,true|nullable|int',
'sell_out_of_stock' => 'required_if:track_quantity,true|boolean',
'category_id' => 'required|int|exists:categories,id',
'price' => 'required|numeric|min:0',
'cost' => 'sometimes|nullable|numeric',
'discounted_price' => 'sometimes|nullable|numeric',
'status' => 'required|string|in:active,draft,review',
'images' => 'sometimes|nullable|array',
];
}
Update 方法
public function update(UpdateProductRequest $request, Product $product)
{
$product->update(
$request
->safe()
->collect()
->filter(fn($value) => !is_null($value))
->except(['images'])
->all()
);
$images = $request->file('images');
if ($images !== null) {
foreach ($images as $image) {
Cloudinary::upload($image->getRealPath(), [
'transformation' => [
'width' => '700',
'quality' => 'auto',
'crop' => 'scale',
]
])->getSecurePath();
$product->attachMedia($image);
}
}
return to_route('admin.products.index')->with('message', 'Prodotto aggiornato con successo');
}
答:
惯性会自动将您的表单转换为对象。FormData
https://inertiajs.com/file-uploads#form-data-conversion
当发出包含文件(甚至是嵌套文件)的惯性请求时, Inertia 会自动将请求数据转换为 FormData 对象。此转换是必要的,以便提交 通过 XHR 的 multipart/form-data 请求。
但是 Laravel 不支持该方法,因此您必须使用 .PUT
multipart/form-data
POST
_method: 'put'
https://inertiajs.com/file-uploads#multipart-limitations
使用 multipart/form-data 请求上传文件不是原生的 在某些服务器端框架中使用 PUT、PATCH 或 删除 HTTP 方法。此限制的最简单解决方法是 只需使用 POST 请求上传文件即可。
但是,一些框架,如 Laravel 和 Rails,支持表单 方法欺骗,它允许您使用 POST 上传文件,但 让框架将请求作为 PUT 或 PATCH 请求处理。这 是通过在请求的数据中包含_method属性来完成的。
因此,您的代码应如下所示。我相信 Laravel 会自动将其解释为 PUT 请求。
// Post the form, but tell Laravel to treat it as a PUT request.
post(route('admin.products.update', product), {
_method: 'put',
});
评论
router.post(
, { _method: 'put', data })
评论