Quasar 提供了一种通过 QUploader 组件上传文件的方法。
¥Quasar supplies a way for you to upload files through the QUploader component.
提示
如果你只需要一个输入文件,你可能需要考虑使用 QFile 选择器组件。
¥If all you want is an input file, you might want to consider using QFile picker component instead.
用法(Usage)
¥Usage
警告
QUploader 需要后端服务器来接收文件。以下示例实际上不会上传。
¥QUploader requires a back-end server to receive the files. The examples below will not actually upload.
提示
QUploader 符合 drag and drop
标准。
¥QUploader is drag and drop
compliant.
警告
使用 vee-validate 时,你必须重命名 vee-validate 的 “fieldBagName” 配置才能使 q-uploader 正常工作。
¥When using vee-validate, you have to rename the “fieldBagName” configuration of vee-validate for the q-uploader to work.
设计(Design)
¥Design
上传多个文件(Uploading multiple files)
¥Uploading multiple files
默认情况下,多个文件将单独上传(每个文件一个线程)。如果你希望所有文件都在单个线程中上传,请使用 batch
属性(下面示例中的第二个 QUploader)。
¥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).
限制上传(Restricting upload)
¥Restricting upload
提示
在上面的例子中,我们使用了 accept
属性。它的值必须是以逗号分隔的唯一文件类型说明符列表。映射到原生输入 type=file 元素的 ‘accept’ 属性。更多信息。
¥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.
警告
accept
属性的推荐格式为 <mediatype>/<extension>
。示例:“image/jpeg”, “image/png”.QUploader 底层使用 <input type="file">
,完全依赖主机浏览器来触发文件选择器。如果 accept
属性(应用于输入)不正确,则屏幕上不会显示文件选择器,或者虽然显示文件选择器,但它会接受所有文件类型。
¥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):
添加标题(Adding headers)
¥Adding headers
使用 headers
设置要随上传请求一起发送的附加 XHR 标头。如果你需要嵌入其他字段,还请查看 API 中的 form-fields
属性。
¥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.
提示
这两个 props(headers
和 form-fields
)也可以用作函数((files) => Array
),允许你根据要上传的文件动态设置它们。
¥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.
还有一个 with-credentials
属性,用于在上传过程使用的 XHR 上将 withCredentials
设置为 true
。
¥There is also the with-credentials
property, which sets withCredentials
to true
on the XHR used by the upload process.
处理上传(Handling upload)
¥Handling upload
提示
你还可以通过 headers
和 method
属性自定义 HTTP 标头和 HTTP 方法。检查 QUploader API 部分。
¥You can also customize the HTTP headers and HTTP method through headers
and method
props. Check QUploader API section.
工厂函数(Factory function)
¥Factory function
你可以使用 factory
属性,该属性必须是一个函数。此函数可以返回一个对象或一个解析为对象的 Promise(如果 Promise 失败,则会发出 @factory-failed
事件)。
¥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 (and in case the Promise fails, @factory-failed
event is emitted).
上面描述的对象可以覆盖以下 QUploader 属性:url
, method
, headers
, formFields
, fieldName
, withCredentials
, sendRaw
).此对象的 props 也可以是函数(形式为 (file[s]) => value
):
¥The Object described above can override the following QUploader props: url
, method
, headers
, formFields
, fieldName
, withCredentials
, sendRaw
). The props of this Object can be Functions as well (of form (file[s]) => value
):
你还可以使用 factory
函数属性并立即返回相同的对象。如果你想同时设置多个 props(如上所述),这将非常有用:
¥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:
插槽(Slots)
¥Slots
在下面的示例中,我们展示了与默认标题等效的效果。另请注意一些你可以使用的布尔作用域属性:scope.canAddFiles
, scope.canUpload
, scope.isUploading
.
¥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
.
警告
请注意,你必须安装并使用另一个组件 (QUploaderAddTrigger) 才能将文件添加到队列。此组件需要放置在具有 position: relative
的 DOM 节点下(提示:QBtn 已经具有 position: relative
),并且会在用户点击其父节点时自动注入必要的事件(请勿手动添加 @click="scope.pickFiles"
)。如果触发器不起作用,请检查其上方是否渲染了元素,并相应地更改 QUploaderAddTrigger 的 zIndex。
¥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.
服务器端点示例(Server endpoint examples)
¥Server endpoint examples
QUploader 默认使用 HTTP(S) 协议上传文件(但并不局限于此,你将在下一节中看到)。
¥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).
提示
它并非必须使用 Nodejs 服务器、Spring 或 ASP.NET(如下所示) - 你可以根据需要处理文件上传,只要你使用的方法符合 HTTP 协议即可。使用 PHP 的示例。
¥It is by no means required to use a Nodejs 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.
Nodejs(Nodejs)
以下是一个用 Nodejs 编写的基本服务器示例。它除了接收文件外什么也不做,所以可以将其作为一个起点。
¥Below is a basic server example written in Nodejs. It does nothing other than receiving the files, so consider it as a starting point.
import fs from 'node:fs'
import path from 'node:path'
import express from 'express'
import formidable from 'formidable'
import throttle from 'express-throttle-bandwidth'
const app = express()
const port = process.env.PORT || 4444
const folder = fileURLToPath(new URL('./files', import.meta.url))
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(ASP.NET MVC/Core)
QUploader 与 Microsoft ASP.NET MVC/Core 2.x Web API 后端无缝集成。在你的 Vue 文件中,使用所需的 Web API 端点配置 QUploader 组件:
¥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:
<q-uploader
url="http://localhost:4444/fileuploader/upload"
label="Upload"
style="max-width: 300px"
/>
如果你的服务器需要身份验证(例如 JWT 令牌),请使用 QUploader 的工厂函数指定 QUploader 将使用的 xhr 标头。例如:
¥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:
<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 控制器:
¥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)
¥Spring
以下是一个 弹簧 示例。属性 fieldName="file"
与 @RequestPart(value = "file")
映射。
¥Below is a Spring 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 />
Python/Flask(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)
# 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/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)
¥Supporting other services
QUploader 目前支持通过 HTTP(S) 协议上传。但是你也可以扩展组件以支持其他服务。例如 Firebase。以下是操作方法。
¥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.
感谢帮助
我们非常乐意接受支持其他上传服务的 PR,以便其他人也能从中受益。点击此页面底部的 Edit this page in browser
链接或页面顶部的铅笔图标。
¥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.
以下是使用你需要提供给 createUploaderComponent()
Quasar 实用程序的 API 的示例。这将创建一个 Vue 组件,你可以在应用中导入它。
¥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.
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 实现,请查看 源代码。
¥For the default XHR implementation in the form of such a plugin, check out source code.
对于 UMD 版本,请使用
Quasar.createUploaderComponent({ ... })
。¥For the UMD version use
Quasar.createUploaderComponent({ ... })
.
然后,你需要在 Vue 中全局注册此组件,或者导入它并将其添加到“components:”中。{}" 在你的 Vue 组件中。
¥Then you register this component globally with Vue or you import it and add it to the “components: {}” in your 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 自动补齐 props 和 slots。
¥If you’re using TypeScript, you’d need to register the new component types to allow Volar to autocomplete props and slots for you.
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>;
}
}