如何在SvelteKit中为表单追加图片文件名?技术方案咨询
解决方案:追加文件名到表单,无需立刻改用全状态管理
你当前的场景完全可以保留默认HTML表单,同时把Ajax上传得到的文件名追加到表单中提交,不需要直接切换成全状态管理表单。下面是具体实现步骤和修改后的代码:
1. 存储上传后的文件名
在脚本里新增一个数组,专门保存后端返回的图片文件名:
let uploadedImageNames: string[] = []; // 新增:存储上传后的文件名
然后修改uploadImage函数,把后端返回的文件名存入这个数组:
const uploadImage = async (file: Blob) => { let formdata = new FormData(); formdata.append('image', file); const res = await fetch('/api/upload', { method: 'POST', body: formdata }); const resp = await res.json(); uploadedImageNames = [...uploadedImageNames, resp.filename]; // 假设后端返回的字段是filename console.log({ resp }); };
2. 在表单中添加隐藏字段传递文件名
在表单内部(比如图片预览区域之后)添加隐藏的input字段,把每个文件名作为表单参数传递:
<div class="flex h-full flex-row my-4 mx-2"> {#if images.length > 0} {#each images as image, index} <div class="h-64 mx-2"> <img class="h-64" src={image} /> <button type="button" on:click={() => removeImage(index)}>Remove</button> <!-- 新增:隐藏字段,传递对应文件名 --> <input type="hidden" name="productImageNames" value={uploadedImageNames[index]} /> </div> {/each} {/if} </div>
3. 实现移除图片的逻辑
添加removeImage函数,同时移除预览图和对应的文件名:
const removeImage = (index: number) => { // 移除预览图 images = images.filter((_, i) => i !== index); // 移除对应的文件名 uploadedImageNames = uploadedImageNames.filter((_, i) => i !== index); // 可选:如果需要删除服务器上的图片,这里可以调用删除API };
4. 后端接收文件名
你的/products/add后端Action里,就可以通过表单数据获取productImageNames数组(比如在SvelteKit Actions里用request.formData().then(data => data.getAll('productImageNames')))。
什么时候需要改用全状态管理表单?
如果后续你的表单需要:
- 实时字段验证
- 复杂的字段联动逻辑
- 多步骤表单
- 完全控制提交流程(比如先做多个异步请求再提交)
这时可以考虑把所有表单字段绑定到组件变量,提交时手动构造FormData,用fetch提交而不是依赖默认表单提交。但当前场景下,混合方式更轻量高效。
修改后的完整代码示例
<script lang="ts"> import { enhance } from '$app/forms'; import type { ActionData } from './$types'; let input: HTMLInputElement; let images: string[] = []; let uploadedImageNames: string[] = []; // 新增:存储上传后的文件名 let form: ActionData; const uploadImage = async (file: Blob) => { let formdata = new FormData(); formdata.append('image', file); const res = await fetch('/api/upload', { method: 'POST', body: formdata }); const resp = await res.json(); uploadedImageNames = [...uploadedImageNames, resp.filename]; // 存入文件名 console.log({ resp }); }; const onChange = async () => { const imgFiles = input.files; if (imgFiles) { for (let i = 0; i < imgFiles.length; i++) { const reader = new FileReader(); reader.addEventListener('load', () => { if (typeof reader.result == 'string') { images = [...images, reader.result]; } }); reader.readAsDataURL(imgFiles[i]); await uploadImage(imgFiles[i]); } } }; // 新增:移除图片逻辑 const removeImage = (index: number) => { images = images.filter((_, i) => i !== index); uploadedImageNames = uploadedImageNames.filter((_, i) => i !== index); // 可选:调用删除图片的API // fetch(`/api/upload/${uploadedImageNames[index]}`, { method: 'DELETE' }); }; </script> <form use:enhance class="flex rounded-lg shadow-lg my-10 sm:w-1/2 md:w-2/4 flex-col mx-auto p-2 sm:p-6" method="post" action="/products/add" > {#if form && form.success} <p>Product Added Successfully</p> {/if} <h2 class="text-2xl font-bold">Add a product / service</h2> <div class="my-4"> <label for="prodSer">Is this a product or a service?</label> <select name="type"> <option value="product">Product</option> <option value="service">Service</option> </select> </div> <input class="basicInput" name="name" placeholder="Name of your product" /> <textarea class="textArea" rows={5} name="description" placeholder="Write a description about it" /> <div class="h-full"> <h4>Images</h4> <label for="imagePicker">Choose images</label> <input multiple bind:this={input} on:change={onChange} id="imagePicker" name="productImages" type="file" placeholder="Upload media" /> <div class="flex h-full flex-row my-4 mx-2"> {#if images.length > 0} {#each images as image, index} <div class="h-64 mx-2"> <img class="h-64" src={image} /> <button type="button" on:click={() => removeImage(index)}>Remove</button> <!-- 新增:隐藏字段传递文件名 --> <input type="hidden" name="productImageNames" value={uploadedImageNames[index]} /> </div> {/each} {/if} </div> </div> <div> <label for="currency">Currency</label> <select id="currency" name="currency"> <option value="Rs.">Rupees</option> </select> </div> <div class="flex flex-row justify-between"> <input class="basicInput halfInput" min={0} name="mrp" placeholder="MRP" type="number" /> <input class="basicInput halfInput" min={0} name="price" placeholder="Price" type="number" /> <!-- 修正type为number --> </div> <div class="flex flex-row justify-between"> <input class="basicInput halfInput" min={0} name="gst" placeholder="GST %" type="number" /> <input class="basicInput halfInput" min={0} name="cess" placeholder="Cess %" type="number" /> <!-- 修正type为number --> </div> <div class="p-2"> <label class="text-sm text-gray-600" for="taxIncl">Price is Inclusive of tax?</label> <input id="taxIncl" type="checkbox" name="includeTax" /> </div> <div> <input class="basicInput" name="unit" placeholder="Unit" /> </div> <div> <input class="basicInput" name="hsn" placeholder="HSN/SAC" /> </div> <div> <input class="basicInput" name="stock" placeholder="Opening Stock" type="number" /> </div> <input class="my-2 sm:my-4 sm:w-1/2 hover:cursor-pointer text-white bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm px-5 py-2.5 text-center mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" type="submit" value="Add" /> </form> <style> .basicInput { height: 60px; width: 100%; border-radius: 5px; border: 1px solid #3e3e3e; margin-top: 10px; padding: 10px; } .textArea { width: 100%; border-radius: 5px; border: 1px solid #3e3e3e; margin-top: 10px; padding: 10px; } .halfInput { width: 49%; } select { padding: 8px; border-radius: 4px; } </style>
内容的提问来源于stack exchange,提问作者Tushar
相关产品推荐
相关产品推荐

