---
title: Uploader
related:
  - title: File Picker
    path: file.md
---
Quasar supplies a way for you to upload files through the QUploader component.

> [!TIP]
> If all you want is an input file, you might want to consider using [QFile](file.md) picker component instead.

## QUploader API

### Props

- `factory` (Function, optional)
  Function which should return an Object or a Promise resolving with an Object; For best performance, reference it from your scope and do not define it inline
  Function signature: `(files?: any[]) => object | Promise<any>`
  Params:
    - `files` (any[], optional)
      Uploaded files
  Returns: `object | Promise<any>`
    Optional configuration for the upload process; You can override QUploader props in this Object (url, method, headers, formFields, fieldName, withCredentials, sendRaw); Props of these Object can also be Functions with the form of (file[s]) => value
- `url` (string | Function, optional)
  URL or path to the server which handles the upload. Takes String or factory function, which returns String. Function is called right before upload; If using a function then for best performance, reference it from your scope and do not define it inline
  Function signature: `(files?: any[]) => string`
  Examples: `'https://example.com/path'`, `files => `https://example.com?count=${ files.length }``
  Params:
    - `files` (any[], optional)
      Uploaded files
  Returns: `string`
    URL or path to the server which handles the upload
- `method` (string | Function, optional), default `'POST'`
  HTTP method to use for upload; Takes String or factory function which returns a String; Function is called right before upload; If using a function then for best performance, reference it from your scope and do not define it inline
  Function signature: `(files?: any[]) => string`
  Accepts: `'POST'`, `'PUT'`
  Examples: `'POST'`, `files => (files.length > 10 ? 'POST' : 'PUT')`
  Params:
    - `files` (any[], optional)
      Uploaded files
  Returns: `string`
    HTTP method to use for upload
- `field-name` (string | Function, optional), default `file => file.name`
  Field name for each file upload; This goes into the following header: 'Content-Disposition: form-data; name="__HERE__"; filename="somefile.png"; If using a function then for best performance, reference it from your scope and do not define it inline
  Function signature: `(files?: File) => string`
  Examples: `'backgroundFile'`, `file => ('background' + file.name)`
  Params:
    - `files` (File, optional)
      The current file being processed
  Returns: `string`
    Field name for the current file upload
- `headers` (any[] | Function, optional)
  Array or a factory function which returns an array; Array consists of objects with header definitions; Function is called right before upload; If using a function then for best performance, reference it from your scope and do not define it inline
  Function signature: `(files?: any[]) => any[]`
  Examples:
    - `[{ name: 'Content-Type', value: 'application/json' }, { name: 'Accept', value: 'application/json' }]`
    - `() => [ { name: 'X-Custom-Timestamp', value: Date.now() }]`
    - `files => [ { name: 'X-Custom-Count', value: files.length }]`
  Params:
    - `files` (any[], optional)
      Uploaded files
  Returns: `any[]`
    An array consisting of objects with header definitions
  Object shape:
    - `name` (string, required)
      Header name
      Examples: `'Content-Type'`, `'Accept'`, `'Cache-Control'`
    - `value` (string, required)
      Header value
      Examples: `'application/json'`, `'no-cache'`
- `form-fields` (any[] | Function, optional)
  Array or a factory function which returns an array; Array consists of objects with additional fields definitions (used by Form to be uploaded); Function is called right before upload; If using a function then for best performance, reference it from your scope and do not define it inline
  Function signature: `(files?: any[]) => any[]`
  Examples:
    - `[{ name: 'my-field', value: 'my-value' }]`
    - `() => [ { name: 'my-field', value: 'my-value' }]`
    - `files => [ { name: 'my-field', value: 'my-value' + files.length }]`
  Params:
    - `files` (any[], optional)
      Uploaded files
  Returns: `any[]`
    An array consisting of objects with additional field definitions (used by FormData to be uploaded)
  Object shape:
    - `name` (string, required)
      Field name
      Examples: `'Some field'`
    - `value` (string, required)
      Field value
      Examples: `'some-value'`
- `with-credentials` (boolean | Function, optional)
  Sets withCredentials to true on the XHR that manages the upload; Takes boolean or factory function for Boolean; Function is called right before upload; If using a function then for best performance, reference it from your scope and do not define it inline
  Function signature: `(files?: any[]) => boolean`
  Examples: `true`, `files => (files.length === 2)`
  Params:
    - `files` (any[], optional)
      Uploaded files
  Returns: `boolean`
    If true, withCredentials will be set to true on the XHR that manages the upload
- `send-raw` (boolean | Function, optional)
  Send raw files without wrapping into a Form(); Takes boolean or factory function for Boolean; Function is called right before upload; If using a function then for best performance, reference it from your scope and do not define it inline
  Function signature: `(files?: any[]) => boolean`
  Examples: `true`, `files => (files.length > 2)`
  Params:
    - `files` (any[], optional)
      Uploaded files
  Returns: `boolean`
    If true, raw files will get sent without wrapping into a Form()
- `batch` (boolean | Function, optional)
  Upload files in batch (in one XHR request); Takes boolean or factory function for Boolean; Function is called right before upload; If using a function then for best performance, reference it from your scope and do not define it inline
  Function signature: `(files?: any[]) => boolean`
  Examples: `files => files.length > 10`
  Params:
    - `files` (any[], optional)
      Uploaded files
  Returns: `boolean`
    If true, files will be uploaded in a batch (in one XHR request)
- `multiple` (boolean, optional)
  Allow multiple file uploads
- `accept` (string, optional)
  Comma separated list of unique file type specifiers. Maps to 'accept' attribute of native input type=file element
  Examples:
    - `'.jpg, .pdf, image/*'`
    - `'image/jpeg, .pdf'`
- `capture` (string, optional)
  Optionally, specify that a new file should be captured, and which device should be used to capture that new media of a type defined by the 'accept' prop. Maps to 'capture' attribute of native input type=file element
  Accepts: `'user'`, `'environment'`
- `max-file-size` (number | string, optional)
  Maximum size of individual file in bytes
  Examples: `1024`, `'1048576'`
- `max-total-size` (number | string, optional)
  Maximum size of all files combined in bytes
- `max-files` (number | string, optional)
  Maximum number of files to contain
- `filter` (Function, optional)
  Custom filter for added files; Only files that pass this filter will be added to the queue and uploaded; For best performance, reference it from your scope and do not define it inline
  Function signature: `(files?: any[]) => any[]`
  Examples: `files => files.filter(file => file.size === 1024)`
  Params:
    - `files` (any[], optional)
      Candidate files to be added to queue
  Returns: `any[]`
    Filtered files to be added to queue
- `label` (string, optional)
  Label for the uploader
  Examples: `'Upload photo here'`
- `color` (string, optional)
  Color name for component from the Quasar Color Palette
  Examples: `'primary'`, `'teal'`, `'teal-10'`
- `text-color` (string, optional)
  Overrides text color (if needed); Color name from the Quasar Color Palette
  Examples: `'primary'`, `'teal'`, `'teal-10'`
- `dark` (boolean, optional), default `null`
  Notify the component that the background is a dark color
- `square` (boolean, optional)
  Removes border-radius so borders are squared
- `flat` (boolean, optional)
  Applies a 'flat' design (no default shadow)
- `bordered` (boolean, optional)
  Applies a default border to the component
- `no-thumbnails` (boolean, optional)
  Don't display thumbnails for image files
- `auto-upload` (boolean, optional)
  Upload files immediately when added
- `hide-upload-btn` (boolean, optional)
  Don't show the upload button
- `thumbnail-fit` (string, optional), default `'cover'` *(added v2.17)*
  How the thumbnail image will fit into the container; Equivalent of the background-size prop
  Examples: `'cover'`, `'contain'`, `'auto'`, `'50%'`
- `disable` (boolean, optional)
  Put component in disabled mode
- `readonly` (boolean, optional)
  Put component in readonly mode

### Computed Props

- `files` (any[], optional)
  List of all files
- `queuedFiles` (any[], optional)
  List of files that are waiting to be uploaded
- `uploadedFiles` (any[], optional)
  List of files that have been uploaded
- `uploadedSize` (number, optional)
  Size of all uploaded files in bytes
- `uploadSizeLabel` (string, optional)
  Label for the size total of all files
  Examples: `'1.0MB'`
- `uploadProgressLabel` (string, optional)
  Label for the upload progress (in %)
  Examples: `'52.76%'`
- `canAddFiles` (boolean, optional)
  Whether new files can be added to the list
- `canUpload` (boolean, optional)
  Whether the files can be uploaded
- `isBusy` (boolean, optional)
  The component state is set as busy; User should not be able to interact with the component
- `isUploading` (boolean, optional)
  The component is uploading files

### Methods

- `pickFiles(evt: Event): void`
  Trigger the file picker dialog; The event must come from a user interaction event handler
  Params:
    - `evt` (Event, required)
      JS event object of the original user interaction handler
- `addFiles(files: any[] | FileList): void`
  Add files programmatically
  Params:
    - `files` (any[] | FileList, required)
      Array of files (instances of File) or FileList
- `upload(): void`
  Start uploading (same as clicking the upload button)
- `abort(): void`
  Abort upload of all files (same as clicking the abort button)
- `reset(): void`
  Resets uploader to default; Empties queue, aborts current uploads
- `removeUploadedFiles(): void`
  Removes already uploaded files from the list
- `removeQueuedFiles(): void`
  Remove files that are waiting for upload to start (same as clicking the left clear button)
- `removeFile(file: File): void`
  Remove specified file from the queue
  Params:
    - `file` (File, required)
      The file to remove
- `updateFileStatus(file: File, status: string, uploadedSize: number): void`
  Update the status of a file
  Params:
    - `file` (File, required)
      The file to update
    - `status` (string, required)
      Status of file
      Accepts: `'idle'`, `'failed'`, `'uploading'`, `'uploaded'`
    - `uploadedSize` (number, required)
      The number of uploaded bytes of the file; Is required explicitly only when status is NOT 'uploaded'
- `isAlive(): boolean`
  Is the component alive (activated but not unmounted); Useful to determine if you still need to compute anything going further
  Returns: `boolean`
    If true, the current component is still activated and mounted

### Events

- `@uploaded`
  Emitted when file or batch of files is uploaded
  Params:
    - `info` (object, optional)
      Object containing information about the event
      Object shape:
        - `files` (any[], required)
          Uploaded files
        - `xhr` (object, required)
          XMLHttpRequest that has been used to upload this batch of files
- `@failed`
  Emitted when file or batch of files has encountered error while uploading
  Params:
    - `info` (object, optional)
      Object containing information about the event
      Object shape:
        - `files` (any[], required)
          Files which encountered error
        - `xhr` (object, required)
          XMLHttpRequest that has been used to upload this batch of files
- `@uploading`
  Emitted when file or batch of files started uploading
  Params:
    - `info` (object, optional)
      Object containing information about the event
      Object shape:
        - `files` (any[], required)
          Files which are now uploading
        - `xhr` (object, required)
          XMLHttpRequest used for uploading
- `@factory-failed`
  Emitted when the factory function throws, returns an invalid value, or supplies a Promise which is rejected or aborted
  Params:
    - `err` (Error, optional)
      Error object which is the Promise rejection reason
    - `files` (any[], optional)
      Files which were to get uploaded
- `@rejected`
  Emitted after files are picked and some do not pass the validation props (accept, max-file-size, max-total-size, filter, etc)
  Params:
    - `rejectedEntries` (any[], optional)
      Array of { failedPropValidation: string, file: File } Objects for files that do not pass the validation
- `@added`
  Emitted when files are added into the list
  Params:
    - `files` (any[], optional)
      Array of files that were added
- `@removed`
  Emitted when files are removed from the list
  Params:
    - `files` (any[], optional)
      Array of files that were removed
- `@start`
  Started working
- `@finish`
  Finished working (regardless of success or fail)

### Scoped Slots

- `#header`
  Slot for custom header; Scope is the QUploader instance itself
  Scope:
    - `...self` (ComponentInstance, optional)
      QUploader instance
- `#list`
  Slot for custom list; Scope is the QUploader instance itself
  Scope:
    - `...self` (ComponentInstance, optional)
      QUploader instance

## Usage

> [!IMPORTANT]
> QUploader requires a back-end server to receive the files. The examples below will not actually upload.

> [!NOTE]
> QUploader is `drag and drop` compliant.

> [!IMPORTANT]
> When using vee-validate, you have to rename the "fieldBagName" configuration of vee-validate for the q-uploader to work.

### Design

Example "Basic":

```vue
<template>
  <div class="q-gutter-sm row items-start">
    <q-uploader url="http://localhost:4444/upload" style="max-width: 300px" />

    <q-uploader
      url="http://localhost:4444/upload"
      color="teal"
      flat
      bordered
      style="max-width: 300px"
    />

    <q-uploader
      url="http://localhost:4444/upload"
      label="Upload files"
      color="purple"
      square
      flat
      bordered
      style="max-width: 300px"
    />

    <q-uploader
      url="http://localhost:4444/upload"
      label="No thumbnails"
      color="amber"
      text-color="black"
      no-thumbnails
      style="max-width: 300px"
    />
  </div>
</template>
```

Example "Force dark mode":

```vue
<template>
  <div style="max-width: 300px">
    <q-uploader url="http://localhost:4444/upload" dark />
  </div>
</template>
```

### Uploading multiple files

By default, multiple files will be uploaded individually (one thread per file). Should you want all files to be uploaded in a single thread, use the `batch` property (second QUploader in the example below).

Example "Multiple":

```vue
<template>
  <div class="q-gutter-sm row items-start">
    <q-uploader
      url="http://localhost:4444/upload"
      label="Individual upload"
      multiple
      style="max-width: 300px"
    />

    <q-uploader
      url="http://localhost:4444/upload"
      label="Batch upload"
      multiple
      batch
      style="max-width: 300px"
    />
  </div>
</template>
```

### Restricting upload

Example "Basic restrictions":

```vue
<template>
  <div class="q-gutter-md row items-start">
    <q-uploader
      style="max-width: 300px"
      url="http://localhost:4444/upload"
      label="Restricted to images"
      multiple
      accept=".jpg, image/*"
      @rejected="onRejected"
    />

    <q-uploader
      style="max-width: 300px"
      url="http://localhost:4444/upload"
      label="Max file size (2k)"
      multiple
      max-file-size="2048"
      @rejected="onRejected"
    />

    <q-uploader
      style="max-width: 300px"
      url="http://localhost:4444/upload"
      label="Max total upload size (4k)"
      multiple
      max-total-size="4096"
      @rejected="onRejected"
    />

    <q-uploader
      style="max-width: 300px"
      url="http://localhost:4444/upload"
      label="Max number of files (3)"
      multiple
      max-files="3"
      @rejected="onRejected"
    />
  </div>
</template>

<script setup>
import { useQuasar } from 'quasar'

const $q = useQuasar()

function onRejected(rejectedEntries) {
  // Notify plugin needs to be installed
  // https://v2.quasar.dev/quasar-plugins/notify#Installation
  $q.notify({
    type: 'negative',
    message: `${rejectedEntries.length} file(s) did not pass validation constraints`
  })
}
</script>
```

> [!NOTE]
> In the example above, we're using `accept` property. Its value must be a comma separated list of unique file type specifiers. Maps to 'accept' attribute of native input type=file element. [More info](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers).

> [!WARNING]
> Recommended format for the `accept` property is `<mediatype>/<extension>`. Examples: "image/jpeg", "image/png". QUploader uses an `<input type="file">` under the hood and it relies entirely on the host browser to trigger the file picker. If the `accept` property (that gets applied to the input) is not correct, no file picker will appear on screen or it will appear but it will accept all file types.

You can also apply custom filters (which are executed after user picks files):

Example "Filter":

```vue
<template>
  <div class="q-gutter-md row items-start">
    <q-uploader
      style="max-width: 300px"
      url="http://localhost:4444/upload"
      label="Filtered (for <2k size)"
      multiple
      :filter="checkFileSize"
      @rejected="onRejected"
    />

    <q-uploader
      style="max-width: 300px"
      url="http://localhost:4444/upload"
      label="Filtered (png only)"
      multiple
      :filter="checkFileType"
      @rejected="onRejected"
    />
  </div>
</template>

<script setup>
import { useQuasar } from 'quasar'

function checkFileSize(files) {
  return files.filter(file => file.size < 2048)
}

function checkFileType(files) {
  return files.filter(file => file.type === 'image/png')
}

const $q = useQuasar()

function onRejected(rejectedEntries) {
  // Notify plugin needs to be installed
  // https://v2.quasar.dev/quasar-plugins/notify#Installation
  $q.notify({
    type: 'negative',
    message: `${rejectedEntries.length} file(s) did not pass validation constraints`
  })
}
</script>
```

### Adding headers

Use `headers` for setting additional XHR headers to be sent along the upload request. Also check `form-fields` prop in the API, if you need additional fields to be embedded.

Example "Headers":

```vue
<template>
  <q-uploader
    url="http://localhost:4444/upload"
    :headers="[{ name: 'X-Custom-Timestamp', value: '1550240306080' }]"
    style="max-width: 300px"
  />
</template>
```

> [!TIP]
> These two props (`headers` and `form-fields`) can be used as a function too (`(files) => Array`), allowing you to dynamically set them based on the files that are to be uploaded.

There is also the `with-credentials` property, which sets `withCredentials` to `true` on the XHR used by the upload process.

### Handling upload

Example "Auto upload on file selection":

```vue
<template>
  <q-uploader
    label="Auto Uploader"
    auto-upload
    url="http://localhost:4444/upload"
    multiple
  />
</template>
```

Example "Custom upload URL":

```vue
<template>
  <q-uploader label="Auto Uploader" auto-upload :url="getUrl" multiple />
</template>

<script setup>
function getUrl(files) {
  return `http://localhost:4444/upload?count=${files.length}`
}
</script>
```

> [!TIP]
> You can also customize the HTTP headers and HTTP method through `headers` and `method` props. Check QUploader API section.

### Factory function

There is a `factory` prop you can use which must be a Function. This function can return either an Object or a Promise resolving with an Object. The `@factory-failed` event is emitted if the function throws, returns an invalid value, or returns a Promise which is rejected or aborted.

The Object described above can override the following QUploader props: `url`, `method`, `headers`, `formFields`, `fieldName`, `withCredentials`, and `sendRaw`. The props of this Object can be Functions as well (of form `(file[s]) => value`):

Example "Promise-based factory function":

```vue
<template>
  <q-uploader :factory="factoryFn" multiple style="max-width: 300px" />
</template>

<script setup>
function factoryFn(files) {
  // returning a Promise

  return new Promise(resolve => {
    // simulating a delay of 2 seconds
    setTimeout(() => {
      resolve({
        url: 'http://localhost:4444/upload'
      })
    }, 2000)
  })
}
</script>
```

You can also use the `factory` Function prop and return immediately the same Object. This is useful if you want to set multiple props (described above) simultaneously:

Example "Immediate return factory function":

```vue
<template>
  <q-uploader :factory="factoryFn" multiple style="max-width: 300px" />
</template>

<script setup>
function factoryFn(files) {
  return {
    url: 'http://localhost:4444/upload',
    method: 'POST'
  }
}
</script>
```

### Slots

In the example below we're showing the equivalent of the default header. Also notice some Boolean scope properties that you can use: `scope.canAddFiles`, `scope.canUpload`, `scope.isUploading`.

> [!IMPORTANT]
> Notice that you must install and use one more component (QUploaderAddTrigger) in order to be able to add files to the queue. This component needs to be placed under a DOM node which has `position: relative` (hint: QBtn has it already) and will automatically inject the necessary events when user clicks on its parent (do NOT manually add `@click="scope.pickFiles"`). If the trigger is not working, check if you have an element rendered above it and change the zIndex of QUploaderAddTrigger accordingly.

Example "Custom header":

```vue
<template>
  <q-uploader
    url="http://localhost:4444/upload"
    label="Custom header"
    multiple
  >
    <template #header="scope">
      <div class="row no-wrap items-center q-pa-sm q-gutter-xs">
        <q-btn
          v-if="scope.queuedFiles.length > 0"
          icon="clear_all"
          @click="scope.removeQueuedFiles"
          round
          dense
          flat
        >
          <q-tooltip>Clear All</q-tooltip>
        </q-btn>
        <q-btn
          v-if="scope.uploadedFiles.length > 0"
          icon="done_all"
          @click="scope.removeUploadedFiles"
          round
          dense
          flat
        >
          <q-tooltip>Remove Uploaded Files</q-tooltip>
        </q-btn>
        <q-spinner v-if="scope.isUploading" class="q-uploader__spinner" />
        <div class="col">
          <div class="q-uploader__title">Upload your files</div>
          <div class="q-uploader__subtitle"
            >{{ scope.uploadSizeLabel }} /
            {{ scope.uploadProgressLabel }}</div
          >
        </div>
        <q-btn
          v-if="scope.canAddFiles"
          type="a"
          icon="add_box"
          @click="scope.pickFiles"
          round
          dense
          flat
        >
          <q-uploader-add-trigger />
          <q-tooltip>Pick Files</q-tooltip>
        </q-btn>
        <q-btn
          v-if="scope.canUpload"
          icon="cloud_upload"
          @click="scope.upload"
          round
          dense
          flat
        >
          <q-tooltip>Upload Files</q-tooltip>
        </q-btn>

        <q-btn
          v-if="scope.isUploading"
          icon="clear"
          @click="scope.abort"
          round
          dense
          flat
        >
          <q-tooltip>Abort Upload</q-tooltip>
        </q-btn>
      </div>
    </template>
  </q-uploader>
</template>
```

Example "Custom files list":

```vue
<template>
  <div style="max-width: 300px">
    <q-uploader url="http://localhost:4444/upload" label="Custom list" multiple>
      <template #list="scope">
        <q-list separator>
          <q-item v-for="file in scope.files" :key="file.__key">
            <q-item-section>
              <q-item-label class="full-width ellipsis">
                {{ file.name }}
              </q-item-label>

              <q-item-label caption> Status: {{ file.__status }} </q-item-label>

              <q-item-label caption>
                {{ file.__sizeLabel }} / {{ file.__progressLabel }}
              </q-item-label>
            </q-item-section>

            <q-item-section v-if="file.__img" thumbnail class="gt-xs">
              <img :alt="file.name" :src="file.__img.src" />
            </q-item-section>

            <q-item-section top side>
              <q-btn
                class="gt-xs"
                size="12px"
                flat
                dense
                round
                icon="delete"
                @click="scope.removeFile(file)"
              />
            </q-item-section>
          </q-item>
        </q-list>
      </template>
    </q-uploader>
  </div>
</template>
```

## Accessibility *(v2.25+)*

QUploader's controls are real buttons — adding, uploading, aborting and removing files all go through QBtns that respond to the keyboard as usual — and per-file upload progress is exposed with `progressbar` semantics.

The header and per-file buttons are icon-only, so they carry localized accessible names from the [Quasar Language Pack](../options/quasar-language-packs.md) (`uploader.*`), as does the hidden native file input. The rest still needs your attention when accessibility matters: file status changes (uploading, uploaded, failed, rejected) are conveyed visually only, with no screen reader announcement. Use the `header` slot if you need different wording, and listen to the upload lifecycle events (`@uploaded`, `@failed`, `@rejected`, ...) to feed a live region of your own. Drag-and-drop is a pointer-only convenience — the "add files" button is the keyboard path.

## Server endpoint examples

QUploader works by default with the HTTP(S) protocol to upload files (but it's not limited to it as you'll see in the section following this one).

> [!NOTE]
> It is by no means required to use a Node.js server or Spring or ASP.NET like below -- you can handle file upload however you want, as long as the method you are using fits the HTTP protocol. Example with [PHP](https://secure.php.net/manual/en/features.file-upload.php).

### Node.js

Below is a basic server example written in Node.js. It does nothing other than receiving the files, so consider it as a starting point.

```js
import fs from 'node:fs'
import path from 'node:path'
import express from 'express'
import formidable from 'formidable'

const app = express()

const port = process.env.PORT || 4444
const folder = path.join(import.meta.dirname, 'files')

if (!fs.existsSync(folder)) {
  fs.mkdirSync(folder)
}

app.set('port', port)

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  res.header(
    'Access-Control-Allow-Headers',
    'Origin, X-Requested-With, Content-Type, Accept'
  )
  next()
})

app.post('/upload', (req, res) => {
  const form = formidable({
    uploadDir: folder,
    keepExtensions: true
  })

  form.parse(req, (err, fields, files) => {
    if (err) {
      res.status(400).send('Upload failed')
      return
    }

    console.log('\n-----------')
    console.log('Fields', fields)
    console.log('Received:', Object.keys(files))
    console.log()
    res.send('Thank you')
  })
})

app.listen(port, () => {
  console.log('\nUpload server running on http://localhost:' + port)
})
```

### ASP.NET MVC/Core

QUploader seamlessly integrates with a Microsoft ASP.NET MVC/Core 2.x Web API backend.
In your Vue file, configure the QUploader component with the desired Web API endpoint:

```html
<q-uploader
  url="http://localhost:4444/fileuploader/upload"
  label="Upload"
  style="max-width: 300px"
/>
```

If your server requires authentication such as a JWT token, use QUploader's factory function to specify the xhr header that will be used by QUploader. For example:

```html
<template>
  <q-uploader label="Upload" :factory="factoryFn" style="max-width: 300px" />
</template>

<script setup>
  function factoryFn(file) {
    return new Promise((resolve, reject) => {
      // Retrieve JWT token from your store.
      const token = 'myToken'
      resolve({
        url: 'http://localhost:4444/fileuploader/upload',
        method: 'POST',
        headers: [{ name: 'Authorization', value: `Bearer ${token}` }]
      })
    })
  }
</script>
```

The file(s) payload of QUploader will be a properly formed `IFormFileCollection` object that you can read via your ASP.NET Web API controller's `.Request` property.
ASP.NET Core 2.2 Controller:

```
[Route("api/[controller]")]
[ApiController]
public class FileUploaderController : ControllerBase
{
    [HttpPost]
    public async Task upload()
    {
        // Request's .Form.Files property will
        // contain QUploader's files.
        var files = this.Request.Form.Files;
        foreach (var file in files)
        {
            if (file == null || file.Length == 0)
                continue;

            // Do something with the file.
            var fileName = file.FileName;
            var fileSize = file.Length;
            // save to server...
            // ...
        }
    }
}
```

### Spring

Below is a [Spring](https://spring.io/guides/gs/uploading-files/) example. Attribute `fieldName="file"` is mapping with `@RequestPart(value = "file")`.

```
// java
@RestController
public class UploadRest {
	@PostMapping("/upload")
	public void handleFileUpload(@RequestPart(value = "file") final MultipartFile uploadfile) throws IOException {
		saveUploadedFiles(uploadfile);
	}

	private String saveUploadedFiles(final MultipartFile file) throws IOException {
		final byte[] bytes = file.getBytes();
		final Path path = Paths.get("YOUR_ABSOLUTE_PATH" + file.getOriginalFilename());
		Files.write(path, bytes);
	}
}

// html
<q-uploader field-name="file" url="YOUR_URL_BACK/upload" with-credentials />
```

### PHP/Laravel

Below is a [Laravel](https://laravel.com/docs/master/requests#files) example. Attribute `field-name="image"` is mapping with `$request->file('image')`, which also makes the file directly addressable in Laravel's validation rules.

```
// PHP (controller)
class UploadController extends Controller
{
    public function upload(Request $request)
    {
        $request->validate([
            'image' => 'required|mimes:jpg,jpeg,png|max:2048'
        ]);

        $path = $request->file('image')->store('uploads');

        return $path;
    }
}

// html
<q-uploader field-name="image" url="YOUR_URL_BACK/upload" accept=".jpg, .jpeg, .png" />
```

### Python/Flask

```
// python
from flask import Flask, request
from werkzeug import secure_filename
from flask_cors import CORS
import os

app = Flask(__name__)

# This is necessary because QUploader uses an AJAX request
# to send the file
cors = CORS()
cors.init_app(app, resource={r"/api/*": {"origins": "*"}})

@app.route('/upload', methods=['POST'])
def upload():
    for fname in request.files:
        f = request.files.get(fname)
        print(f)
        f.save('./uploads/%s' % secure_filename(fname))

    return 'Okay!'

if __name__ == '__main__':
    if not os.path.exists('./uploads'):
        os.mkdir('./uploads')
    app.run(debug=True)
```

### Julia/Genie

```
# Julia Genie

using Genie, Genie.Requests, Genie.Renderer

Genie.config.cors_headers["Access-Control-Allow-Origin"]  =  "*"
Genie.config.cors_headers["Access-Control-Allow-Headers"] = "Content-Type"
Genie.config.cors_headers["Access-Control-Allow-Methods"] = "GET,POST,PUT,DELETE,OPTIONS"
Genie.config.cors_allowed_origins = ["*"]

#== server ==#

route("/") do
  "File Upload"
end

route("/upload", method = POST) do
  if infilespayload(:img)                 # :img is file-name
    @info filename(filespayload(:img))    # file-name="img"
    @info filespayload(:img).data

    open("upload/file.jpg", "w") do io
      write(io, filespayload(:img).data)
    end
  else
    @info "No image uploaded"
  end

  Genie.Renderer.redirect(:get)
end

isrunning(:webserver) || up()
```

### Perl/Mojolicious

```
# Perl

use Mojolicious::Lite -signatures;

# CORS
app->hook(after_dispatch => sub {
    my $c = shift;
    $c->res->headers->header('Access-Control-Allow-Origin' => '*');
});
options '*' => sub ($c) {
   $c->res->headers->header('Access-Control-Allow-Methods' => 'GET, OPTIONS, POST, DELETE, PUT');
   $c->res->headers->header('Access-Control-Allow-Headers' => 'Content-Type');
   $c->render(text => '');
};

post '/upload' => sub ($c) {
   my $uploads = $c->req->uploads('files');

   foreach my $f (@{$uploads}) {
      $f->move_to('/tmp/' . $f->filename);
   }

   $c->render(text => 'Saved!');
};

app->start;
```

## Supporting other services

QUploader currently supports uploading through the HTTP(S) protocol. But you can extend the component to support other services as well. Like Firebase for example. Here's how you can do it.

> [!NOTE]
> **Help appreciated**
>
> We'd be more than happy to accept PRs on supporting other upload services as well, so others can benefit. Hit the `Edit this page in browser` link at bottom of this page or the pencil icon at the top of the page.

Below is an example with the API that you need to supply to the `createUploaderComponent()` Quasar util. This will create a Vue component that you can import in your app.

Example "MyUploader.js":

```js
import { createUploaderComponent } from 'quasar'
import { computed } from 'vue'

// export a Vue component
export default createUploaderComponent({
  // defining the QUploader plugin here

  name: 'MyUploader', // your component's name

  props: {
    // ...your custom props
  },

  emits: [
    // ...your custom events name list
  ],

  injectPlugin({ props, emit, helpers }) {
    // can call any other composables here
    // as this function will run in the component's setup()

    // [ REQUIRED! ]
    // We're working on uploading files
    const isUploading = computed(() => {
      // return <Boolean>
    })

    // [ optional ]
    // Shows overlay on top of the
    // uploader signaling it's waiting
    // on something (blocks all controls)
    const isBusy = computed(() => {
      // return <Boolean>
    })

    // [ REQUIRED! ]
    // Abort and clean up any process
    // that is in progress
    function abort() {
      // ...
    }

    // [ REQUIRED! ]
    // Start the uploading process
    function upload() {
      // ...
    }

    return {
      isUploading,
      isBusy,

      abort,
      upload
    }
  }
})
```

> [!NOTE]
>
> - For the default XHR implementation in the form of such a plugin, check out [source code](https://github.com/quasarframework/quasar/blob/dev/ui/src/components/uploader/xhr-uploader-plugin.js).
> - For the UMD version use `Quasar.createUploaderComponent({ ... })`.

Then you register this component globally with Vue or you import it and add it to the "components: {}" in your Vue components.

```js
// globally registering your component in a boot file
import { defineBoot } from '#q-app'
import MyUploader from '../../path/to/MyUploader' // the file from above

export default defineBoot(({ app }) {
  app.component('MyUploader', MyUploader)
})

// or declaring it in a .vue file
import MyUploader from '../../path/to/MyUploader' // the file from above
export default {
  // ...
  components: {
    // ...
    MyUploader
  }
}
```

If you're using TypeScript, you'd need to register the new component types to allow Volar to autocomplete props and slots for you.

```js
import {
  GlobalComponentConstructor,
  QUploaderProps,
  QUploaderSlots,
} from 'quasar';

interface MyUploaderProps extends QUploaderProps {
  // .. add custom props
  freeze: boolean;
  // .. add custom events
  onFreeze: boolean;
}

declare module 'vue' {
  interface GlobalComponents {
    MyUploader: GlobalComponentConstructor<MyUploaderProps, QUploaderSlots>;
  }
}
```
