上传器

QUploader 是 Quasar 提供的文件上传组件。

TIP

如果您只是想要一个输入文件的组件,那么您需要的可能是 QFile 文件选择器组件。

QUploader API

QUploader API


multiple
: Boolean
说明
允许多个文件上传
accept
: String
说明
逗号分隔的唯一文件类型规范列表。映射到原生输入类型为 file 的元素的 'accept' 属性
capture
: String
说明
可选地,指定应捕获新文件,并指定用于捕获由 'accept' 属性定义的新媒体的设备。映射到原生 input type=file 元素的 'capture' 属性。
max-file-size
: Number | String
说明
单个文件的最大大小(以字节为单位)
max-total-size
: Number | String
说明
所有文件组合的最大大小(以字节为单位)
max-files
: Number | String
说明
包含的文件数量上限
filter
: (files) => Array
说明
自定义过滤器用于添加的文件;只有通过此过滤器的文件才会被添加到队列并上传;为了获得最佳性能,请从您的作用域中引用它,不要内联定义。
auto-upload
: Boolean
说明
添加文件时立即上传
hide-upload-btn
: Boolean
说明
不显示上传按钮

用法

WARNING

QUploader 需要一个后端服务器来接收文件。下面的示例不会真正的上传。

TIP

QUploader 是兼容拖拽的。

WARNING

当使用 vee-validate 时,您需要重命名 vee-validate 的 “fieldBagName” 配置以使 q-uploader 正常工作。

设计



<template>
  <div class="q-pa-md">
    <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>
  </div>
</template>

0.0B / 0.00%
0.0B / 0.00%
Upload files
0.0B / 0.00%
No thumbnails
0.0B / 0.00%


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

0.0B / 0.00%

上传多个文件

默认情况下,多个文件的上传时独立的,每个文件使用一个线程。如果您希望所有上传的文件都使用同一个线程,那么使用 batch 属性,见下面第二个示例。



<template>
  <div class="q-pa-md">
    <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>
  </div>
</template>

Individual upload
0.0B / 0.00%
Batch upload
0.0B / 0.00%

限制上传条件



<template>
  <div class="q-pa-md">
    <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>
  </div>
</template>

Restricted to images
0.0B / 0.00%
Max file size (2k)
0.0B / 0.00%
Max total upload size (4k)
0.0B / 0.00%
Max number of files (3)
0.0B / 0.00%

TIP

在上面的示例中,我们使用的是 accept 属性。其值必须是以逗号分隔的唯一文件类型说明符列表。映射到原生 input type=file 标签的 ‘accept’ 属性。更多信息

WARNING

accept 属性的建议格式为 <mediatype>/<extension>。示例:“image/png”, “image/png”。 QUploader 在底层使用了一个 <input type="file">,它完全依赖于浏览器来触发文件选择器。如果 accept 属性(应用于 input)不正确,则不会在屏幕上显示文件选取器,或者它将出现,但它将接受所有文件类型。

您还可以自定义过滤器(在用户选取文件后执行):



<template>
  <div class="q-pa-md">
    <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>
  </div>
</template>

Filtered (for <2k size)
0.0B / 0.00%
Filtered (png only)
0.0B / 0.00%

添加请求头

使用 headers 来设置上传请求的 XHR 请求头。如果您需要嵌入其他字段,那么请查看 API 部分的 form-fields 属性。



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

0.0B / 0.00%

TIP

headersform-fields 属性都可以使用一个函数 ((files) => Array),允许您根据要上传的文件动态的设置他们。

使用 with-credentials 属性,可以将上传过程使用的 XHR 中的 withCredentials 设置为 true

处理上传



<template>
  <div class="q-pa-md">
    <q-uploader
      label="Auto Uploader"
      auto-upload
      url="http://localhost:4444/upload"
      multiple
    />
  </div>
</template>

Auto Uploader
0.0B / 0.00%


<template>
  <div class="q-pa-md">
    <q-uploader
      label="Auto Uploader"
      auto-upload
      :url="getUrl"
      multiple
    />
  </div>
</template>

Auto Uploader
0.0B / 0.00%

TIP

您还可以通过 headersmethod 属性设置 HTTP 请求头和 HTTP 请求方式,请查看 API 部分。

工厂函数

您还可以使用 factory 属性,它必须是一个函数,该函数需返回一个对象或者包裹对象的 Promise(如果该 Promise 失败,则 @factory-failed 事件会被触发)。

上述对象可以重写 QUploader 中的属性:url, method, headers, formFields, fieldName, withCredentials, sendRaw,对象中的字段也可以是一个 (file[s]) => value 格式的函数:



<template>
  <div class="q-pa-md">
    <q-uploader
      :factory="factoryFn"
      multiple
      style="max-width: 300px"
    />
  </div>
</template>

0.0B / 0.00%

您也可以使用 factory 立即返回相同的对象。如果您想同时设置多个属性(如上所述)时,这很有用:



<template>
  <div class="q-pa-md">
    <q-uploader
      :factory="factoryFn"
      multiple
      style="max-width: 300px"
    />
  </div>
</template>

0.0B / 0.00%

插槽

下面的示例中,我们使用插槽实现了与默认头部等价的功能。也要注意一些可能对您有用的布尔类型的属性:scope.canAddFiles, scope.canUpload, scope.isUploading

WARNING

请注意,您必须安装并使用另一个组件 (QUploaderAddTrigger) 才能将文件添加到队列中。该组件需要放置在具有 position: relative 的 DOM 节点下:(提示:QBtn 已经具有它),并且当用户单击其父时,将自动注入必要的事件(请勿手动添加 @click="scope.pickFiles")。如果触发器不工作,请检查在它上面是否覆盖了其他元素,并相应地更改 QUploaderAddTrigger 的 zIndex。



<template>
  <div class="q-pa-md">
    <q-uploader
      url="http://localhost:4444/upload"
      label="Custom header"
      multiple
    >
      <template v-slot: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>
  </div>
</template>

Upload your files
0.0B / 0.00%


<template>
  <div class="q-pa-md" style="max-width: 300px">
    <q-uploader
      url="http://localhost:4444/upload"
      label="Custom list"
      multiple
    >
      <template v-slot: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 :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>

Custom list
0.0B / 0.00%

服务端示例

默认情况下,Quploader 使用 HTTP 协议上传文件(但它不限于此,您将在下面的章节中看到)。

TIP

下面的例子只是示例,并不代表您一定要这样做,您可以以任何您想要的方式处理上传,一个 PHP 的示例。

Nodejs

下面是一个在 Nodejs 编写的基本服务器示例。它只做接收文件的工作,所以把它当作一个起点。

const
  express = require('express'),
  app = express(),
  formidable = require('formidable'),
  path = require('path'),
  fs = require('fs'),
  throttle = require('express-throttle-bandwidth')

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

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

app.set('port', port)
app.use(throttle(1024 * 128)) // throttling bandwidth

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 = new formidable.IncomingForm()

  form.uploadDir = folder
  form.parse(req, (_, fields, files) => {
    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 与 Microsoft ASP.NET MVC/Core 2.x Web API 后端无缝集成。在 Vue 文件中,配置所需的 Web API 链接:

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

如果您的服务器需要身份验证(如 JWT 令牌),请使用 QUploader 的工厂函数指定 QUploadeer 将使用的 xhr 头。例如:

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

<script>
export default {
  methods: {
    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>

QUploader 的文件负载将是格式正确的 IFormFileCollection 对象,您可以通过 ASP.NET Web API 控制器的 .Request 属性读取该对象。 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

下面是一个 Spring 示例。属性 fieldName="file" 正在与 @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 />

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;

支持其他的服务器

QUploader 目前支持通过 HTTP(S) 协议上传。但您也可以扩展组件以支持其他服务。例如 Firebase。下面是您可以做的。

感谢您的帮助

我们很乐意接受支持其他上传服务的 PR,这样其他人也能从中受益。点击此页面顶部右上角的铅笔图标。

下面是一个示例,其中包含需要提供给 createUploaderComponent() Quasar 工具函数 的 API。这将创建一个 Vue 组件,您可以在应用程序中导入它。

// MyUploader.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
    }
  }
})

TIPS

  • 对于这种插件形式的默认 XHR 实现,请查看源代码.
  • 对于 UMD 版本,请使用 Quasar.createUploaderComponent({ ... }).

然后向 Vue 全局注册该组件,或者导入该组件并将其添加到 Vue 组件中的 “components: {}” 中。

// globally registering your component in a boot file
import MyUploader from '../../path/to/MyUploader' // the file from above

export default ({ 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
  }
}

如果您使用的是 TypeScript,则需要注册新的组件类型,以允许 Volar 为您自动补全属性和插槽。

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

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

declare module '@vue/runtime-core' {
  interface GlobalComponents {
    MyUploader: GlobalComponentConstructor<MyUploaderProps, QUploaderSlots>;
  }
}

类型定义

export interface QUploaderHeaderItem {
  name: string;
  value: string;
}
export interface QUploaderFormFieldsItem {
  name: string;
  value: string;
}

type ValueOrFunction<ValueType, Param = never> =
  | ((arg: Param) => ValueType)
  | ValueType;

export type QUploaderFactoryObject = {
  url?: ValueOrFunction<string, readonly File[]>;
  method?: ValueOrFunction<LiteralUnion<"POST" | "PUT">, readonly File[]>;
  headers?: ValueOrFunction<QUploaderHeaderItem[], readonly File[]>;
  formFields?: ValueOrFunction<QUploaderFormFieldsItem[], readonly File[]>;
  fieldName?: ValueOrFunction<string, File>;
  withCredentials?: ValueOrFunction<boolean, readonly File[]>;
  sendRaw?: ValueOrFunction<boolean, readonly File[]>;
};

export type QUploaderFactoryFn = (
  files: readonly File[],
) => QUploaderFactoryObject | Promise<QUploaderFactoryObject>;