소스 검색

xhr-upload: accept a `headers: (file) => {}` function (#2747)

With this patch you can do
```js
uppy.use(XHRUpload, {
  headers: file => ({
    'authorization': `bearer ${global.userToken}`,
    'header-name': file.meta.someMetaValue
  })
})
```
to determine file-specific headers.

The function syntax for `headers` is only available if the `bundle`
option is `false` (the default). `bundle` uploads only use one set of
headers.
Renée Kooi 4 년 전
부모
커밋
fb23bd2a20
4개의 변경된 파일70개의 추가작업 그리고 4개의 파일을 삭제
  1. 19 2
      packages/@uppy/xhr-upload/src/index.js
  2. 33 0
      packages/@uppy/xhr-upload/src/index.test.js
  3. 5 2
      packages/@uppy/xhr-upload/types/index.d.ts
  4. 13 0
      website/src/docs/xhrupload.md

+ 19 - 2
packages/@uppy/xhr-upload/src/index.js

@@ -151,13 +151,26 @@ module.exports = class XHRUpload extends Plugin {
 
   getOptions (file) {
     const overrides = this.uppy.getState().xhrUpload
+    const headers = this.opts.headers
+
     const opts = {
       ...this.opts,
       ...(overrides || {}),
       ...(file.xhrUpload || {}),
       headers: {}
     }
-    Object.assign(opts.headers, this.opts.headers)
+    // Support for `headers` as a function, only in the XHRUpload settings.
+    // Options set by other plugins in Uppy state or on the files themselves are still merged in afterward.
+    //
+    // ```js
+    // headers: (file) => ({ expires: file.meta.expires })
+    // ```
+    if (typeof headers === 'function') {
+      opts.headers = headers(file)
+    } else {
+      Object.assign(opts.headers, this.opts.headers)
+    }
+
     if (overrides) {
       Object.assign(opts.headers, overrides.headers)
     }
@@ -617,7 +630,11 @@ module.exports = class XHRUpload extends Plugin {
       // if bundle: true, we don’t support remote uploads
       const isSomeFileRemote = files.some(file => file.isRemote)
       if (isSomeFileRemote) {
-        throw new Error('Can’t upload remote files when bundle: true option is set')
+        throw new Error('Can’t upload remote files when the `bundle: true` option is set')
+      }
+
+      if (typeof this.opts.headers === 'function') {
+        throw new TypeError('`headers` may not be a function when the `bundle: true` option is set')
       }
 
       return this.uploadBundle(files)

+ 33 - 0
packages/@uppy/xhr-upload/src/index.test.js

@@ -76,4 +76,37 @@ describe('XHRUpload', () => {
       })
     })
   })
+
+  describe('headers', () => {
+    it('can be a function', async () => {
+      const scope = nock('https://fake-endpoint.uppy.io')
+        .defaultReplyHeaders({
+          'access-control-allow-method': 'POST',
+          'access-control-allow-origin': '*',
+          'access-control-allow-headers': 'x-sample-header'
+        })
+      scope.options('/')
+        .reply(200, {})
+      scope.post('/')
+        .matchHeader('x-sample-header', 'test.jpg')
+        .reply(200, {})
+
+      const core = new Core()
+      core.use(XHRUpload, {
+        id: 'XHRUpload',
+        endpoint: 'https://fake-endpoint.uppy.io',
+        headers: (file) => ({
+          'x-sample-header': file.name
+        })
+      })
+      core.addFile({
+        name: 'test.jpg',
+        data: new Blob([Buffer.alloc(8192)])
+      })
+
+      await core.upload()
+
+      expect(scope.isDone()).toBe(true)
+    })
+  })
 })

+ 5 - 2
packages/@uppy/xhr-upload/types/index.d.ts

@@ -2,11 +2,14 @@ import Uppy = require('@uppy/core')
 import XHRUploadLocale = require('./generatedLocale')
 
 declare module XHRUpload {
+  type Headers = {
+    [name: string]: string | number
+  }
   export interface XHRUploadOptions extends Uppy.PluginOptions {
     limit?: number
     bundle?: boolean
     formData?: boolean
-    headers?: any
+    headers?: Headers | ((file: Uppy.UppyFile) => Headers)
     metaFields?: string[]
     fieldName?: string
     timeout?: number
@@ -15,7 +18,7 @@ declare module XHRUpload {
     method?: 'GET' | 'POST' | 'PUT' | 'HEAD' | 'get' | 'post' | 'put' | 'head'
     locale?: XHRUploadLocale
     responseType?: string
-    withCredentials?: boolean    
+    withCredentials?: boolean
   }
 }
 

+ 13 - 0
website/src/docs/xhrupload.md

@@ -86,6 +86,18 @@ headers: {
 }
 ```
 
+Header values can also be derived from file data by providing a function. The function receives a [File Object][File Objects] and must return an object where the keys are header names, and values are header values.
+```js
+headers: (file) => {
+  return {
+    'authorization': `Bearer ${window.getCurrentUserToken()}`,
+    'expires': file.meta.expires
+  }
+}
+```
+
+> ⚠️ The function syntax is not available when the [`bundle: true`](#bundle-false) option is set. `bundle` is disabled by default.
+
 ### `bundle: false`
 
 Send all files in a single multipart request. When `bundle` is set to `true`, [`formData`](#formData-true) must also be set to `true`.
@@ -293,5 +305,6 @@ move_uploaded_file($file_path, $_SERVER['DOCUMENT_ROOT'] . '/img/' . basename($f
 [XHR.timeout]: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/timeout
 [XHR.responseType]: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType
 [uppy.upload-success]: /docs/uppy/#upload-success
+[File Objects]: /docs/uppy/#File-Objects
 [PHP.file-upload]: https://secure.php.net/manual/en/features.file-upload.php
 [PHP.multiple]: https://secure.php.net/manual/en/features.file-upload.multiple.php