Skip to main content
Version: 4.xx.xx

Multipart Upload

We will show you how to multipart upload with refine.

Let's start with the creation form first.

Create Form

Let's add the image field to the post creation form.

pages/posts/create.tsx
import {
useApiUrl,
} from '@refinedev/core'
import {
getValueFromEvent,
Create,
useForm,
} from '@refinedev/antd'
import {
Upload,
Form,
Input,
} from 'antd'

export const PostCreate: React.FC = () => {
const { formProps, saveButtonProps } = useForm<IPost>()

const apiUrl = useApiUrl()

return (
<Create saveButtonProps={saveButtonProps}>
<Form {...formProps} layout="vertical">
<Form.Item
label="Title"
name="title"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="image"
valuePropName="fileList"
getValueFromEvent={getValueFromEvent}
noStyle
>
<Upload.Dragger
name="file"
action={`${apiUrl}/media/upload`}
listType="picture"
maxCount={5}
multiple
>
<p className="ant-upload-text">Drag & drop a file in this area</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
</Create>
)
}

interface IPost {
id: number
title: string
image: [
{
uid: string
name: string
url: string
status: 'error' | 'success' | 'done' | 'uploading' | 'removed'
},
]
}

tip

We can reach the API URL by using the useApiUrl hook.

It will look like this.

multipart upload in a create page

What we need now is an upload end-point that accepts multipart uploads. We write this address in the action property of the Upload component.

[POST] https://api.fake-rest.refine.dev/media/upload
{
"file": "binary"
}
caution

This end-point should be Content-type: multipart/form-data and Form Data: file: binary?.

This end-point should respond similarly.

[POST] https://api.fake-rest.refine.dev/media/upload
{
"url": "https://example.com/uploaded-file.jpeg"
}
multipart upload uploaded item

caution

We have to use the getValueFromEvent method to convert the uploaded files to Antd UploadFile object.

This data is sent to the API when the form is submitted.

[POST] https://api.fake-rest.refine.dev/posts
{
"title": "Test",
"image": [
{
"uid": "rc-upload-1620630541327-7",
"name": "greg-bulla-6RD0mcpY8f8-unsplash.jpg",
"url": "https://refine.ams3.digitaloceanspaces.com/78c82c0b2203e670d77372f4c20fc0e2",
"type": "image/jpeg",
"size": 70922,
"percent": 100,
"status": "done"
}
]
}
caution

The following data are required for the Antd Upload component and all should be saved.

PropertyDescription
uidUnique id
nameFile Name
urlDownload URL
statuserror, success, done, uploading, removed

Edit Form

Let's add the image field to the post editing form.

pages/posts/edit.tsx
import {
useApiUrl,
} from '@refinedev/core'
import {
getValueFromEvent,
Edit,
useForm,
} from '@refinedev/antd'
import {
Upload,
Form,
Input,
} from 'antd'

export const PostEdit: React.FC = () => {
const { formProps, saveButtonProps } = useForm<IPost>()

const apiUrl = useApiUrl()

return (
<Edit saveButtonProps={saveButtonProps}>
<Form {...formProps} layout="vertical">
<Form.Item
label="Title"
name="title"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="image"
valuePropName="fileList"
getValueFromEvent={getValueFromEvent}
noStyle
>
<Upload.Dragger
name="file"
action={`${apiUrl}/media/upload`}
listType="picture"
maxCount={5}
multiple
>
<p className="ant-upload-text">Drag & drop a file in this area</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
</Edit>
)
}
multipart upload in edit page

A request, like the one below, is sent for edit form.

[GET] https://api.fake-rest.refine.dev/posts/1
{
"id": 1,
"title": "Test",
"image": [
{
"uid": "rc-upload-1620630541327-7",
"name": "greg-bulla-6RD0mcpY8f8-unsplash.jpg",
"url": "https://refine.ams3.digitaloceanspaces.com/78c82c0b2203e670d77372f4c20fc0e2",
"type": "image/jpeg",
"size": 70922,
"percent": 100,
"status": "done"
}
]
}

This data is sent to the API when form is submitted.

[PUT] https://api.fake-rest.refine.dev/posts/1
{
"title": "Test",
"image": [
{
"uid": "rc-upload-1620630541327-7",
"name": "greg-bulla-6RD0mcpY8f8-unsplash.jpg",
"url": "https://refine.ams3.digitaloceanspaces.com/78c82c0b2203e670d77372f4c20fc0e2",
"type": "image/jpeg",
"size": 70922,
"percent": 100,
"status": "done"
}
]
}

Uploading State

You may want to disable the "Save" button in the form while the upload is going on. To do this, you can use the useFileUploadState hook.

pages/posts/create.tsx
import { useApiUrl } from '@refinedev/core'
import {
getValueFromEvent,
useFileUploadState,
Create,
useForm,
} from '@refinedev/antd'
import { Upload, Form, Input } from 'antd'

export const PostCreate: React.FC = () => {
const { formProps, saveButtonProps } = useForm<IPost>()

const { isLoading, onChange } = useFileUploadState()

const apiUrl = useApiUrl()

return (
<Create
saveButtonProps={{
...saveButtonProps,
disabled: isLoading,
}}
>
<Form {...formProps} layout="vertical">
<Form.Item
label="Title"
name="title"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="image"
valuePropName="fileList"
getValueFromEvent={getValueFromEvent}
noStyle
>
<Upload.Dragger
name="file"
action={`${apiUrl}/media/upload`}
listType="picture"
maxCount={5}
multiple
onChange={onChange}
>
<p className="ant-upload-text">Drag & drop a file in this area</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
</Create>
)
}

Example

RUN IN YOUR LOCAL
npm create refine-app@latest -- --example upload-antd-multipart