diff --git a/client/js/s3/s3.form.upload.handler.js b/client/js/s3/s3.form.upload.handler.js index 77edafcdf..dafbd8e63 100644 --- a/client/js/s3/s3.form.upload.handler.js +++ b/client/js/s3/s3.form.upload.handler.js @@ -120,7 +120,7 @@ qq.s3.FormUploadHandler = function(options, proxy) { method: method, endpoint: endpoint, params: params, - paramsInBody: true, + paramsMode: qq.paramsMode.paramsInBody, targetName: iframe.name }); diff --git a/client/js/traditional/traditional.form.upload.handler.js b/client/js/traditional/traditional.form.upload.handler.js index 1b95dda06..3845eea60 100644 --- a/client/js/traditional/traditional.form.upload.handler.js +++ b/client/js/traditional/traditional.form.upload.handler.js @@ -63,7 +63,8 @@ qq.traditional.FormUploadHandler = function(options, proxy) { method: method, endpoint: endpoint, params: params, - paramsInBody: options.paramsInBody, + paramsMode: options.paramsMode, + headerParamPrefix: options.headerParamPrefix, targetName: iframe.name }); } diff --git a/client/js/traditional/traditional.xhr.upload.handler.js b/client/js/traditional/traditional.xhr.upload.handler.js index 680c53086..350d0582c 100644 --- a/client/js/traditional/traditional.xhr.upload.handler.js +++ b/client/js/traditional/traditional.xhr.upload.handler.js @@ -15,7 +15,7 @@ qq.traditional.XhrUploadHandler = function(spec, proxy) { getSize = proxy.getSize, getUuid = proxy.getUuid, log = proxy.log, - multipart = spec.forceMultipart || spec.paramsInBody, + multipart = spec.forceMultipart || spec.paramsMode === qq.paramsMode.paramsInBody, addChunkingSpecificParams = function(id, params, chunkData) { var size = getSize(id), @@ -142,7 +142,7 @@ qq.traditional.XhrUploadHandler = function(spec, proxy) { } //build query string - if (!spec.paramsInBody) { + if (spec.paramsMode === qq.paramsMode.paramsInUrl) { if (!multipart) { params[spec.inputName] = name; } @@ -150,13 +150,20 @@ qq.traditional.XhrUploadHandler = function(spec, proxy) { } xhr.open(method, endpoint, true); + if (spec.paramsMode === qq.paramsMode.paramsInHeader) { + var headerParamPrefix = spec.headerParamPrefix || qq.headerParamPrefix; + + qq.each(params, function (key, val) { + xhr.setRequestHeader(headerParamPrefix + key, "" + val); + }); + } if (spec.cors.expected && spec.cors.sendCredentials) { xhr.withCredentials = true; } if (multipart) { - if (spec.paramsInBody) { + if (spec.paramsMode === qq.paramsMode.paramsInBody) { qq.obj2FormData(params, formData); } diff --git a/client/js/upload-handler/form.upload.handler.js b/client/js/upload-handler/form.upload.handler.js index 6413cadc7..d5a951d0d 100644 --- a/client/js/upload-handler/form.upload.handler.js +++ b/client/js/upload-handler/form.upload.handler.js @@ -255,22 +255,22 @@ qq.FormUploadHandler = function(spec) { * that the form is hidden from view. * * @param spec An object containing various properties to be used when constructing the form. Required properties are - * currently: `method`, `endpoint`, `params`, `paramsInBody`, and `targetName`. + * currently: `method`, `endpoint`, `params`, `paramsMode` and `targetName`. * @returns {HTMLFormElement} The created form */ _initFormForUpload: function(spec) { var method = spec.method, endpoint = spec.endpoint, params = spec.params, - paramsInBody = spec.paramsInBody, + paramsMode = spec.paramsMode, targetName = spec.targetName, form = qq.toElement("
"), url = endpoint; - if (paramsInBody) { + if (paramsMode === qq.paramsMode.paramsInBody) { qq.obj2Inputs(params, form); } - else { + else if (paramsMode === qq.paramsMode.paramsInUrl){ url = qq.obj2url(params, endpoint); } diff --git a/client/js/uploader.basic.js b/client/js/uploader.basic.js index ab7425e2a..8ac6949e3 100644 --- a/client/js/uploader.basic.js +++ b/client/js/uploader.basic.js @@ -22,7 +22,7 @@ inputName: "qqfile", method: "POST", params: {}, - paramsInBody: true, + paramsMode: qq.paramsMode.paramsInBody, totalFileSizeName: "qqtotalfilesize", uuidName: "qquuid" }, diff --git a/client/js/util.js b/client/js/util.js index f6d5f109c..375062eb6 100644 --- a/client/js/util.js +++ b/client/js/util.js @@ -184,6 +184,14 @@ var qq = function(element) { (function() { "use strict"; + qq.paramsMode = { + paramsInBody: "paramsInBody", + paramsInHeader: "paramsInHeader", + paramsInUrl: "paramsInUrl" + }; + + qq.headerParamPrefix = ""; + qq.canvasToBlob = function(canvas, mime, quality) { return qq.dataUriToBlob(canvas.toDataURL(mime, quality)); }; diff --git a/client/typescript/fine-uploader.d.ts b/client/typescript/fine-uploader.d.ts index 011a6bddc..c7d1740d8 100644 --- a/client/typescript/fine-uploader.d.ts +++ b/client/typescript/fine-uploader.d.ts @@ -672,14 +672,19 @@ declare namespace FineUploader { */ params?: any; /** - * Enable or disable sending parameters in the request body. + * Configure the mode parameters are added to the request. * - * If `false`, parameters are sent in the URL. - * Otherwise, parameters are sent in the request body + * `qq.paramsMode.paramsInBody` sends params in the request body. + * `qq.paramsMode.paramsInUrl` sends params in the request url. + * `qq.paramsMode.paramsInHeader` sends params in the request header. * - * @default `true` + * @default `qq.paramsMode.paramsInBody` + */ + paramsMode?: string; + /** + * The prefix for parameters sent in the request's header. Only matters if `paramsMode` is set to `qq.paramsMode.paramsInHeader` */ - paramsInBody?: boolean; + headerParamPrefix?: string; /** * The name of the parameter the uniquely identifies each associated item. The value is a Level 4 UUID * diff --git a/docs/features/request-parameters.jmd b/docs/features/request-parameters.jmd index d8d57043c..b85ce00e4 100644 --- a/docs/features/request-parameters.jmd +++ b/docs/features/request-parameters.jmd @@ -95,8 +95,9 @@ To change parameters again, call `setParams` again with the associated `id`.""", Sometimes it may prove useful to send request parameters in the URL query string, and fortunately Fine Uploader supports this right out of the -box. By disabling the `request.paramsInBody` option, you can pass parameters -through the query string rather than the request body. +box. By setting the `request.paramsInUrl` option, you can pass parameters +through the query string rather than the request body. Alternatively `request.paramsInHeader` +adds parameters in the request header, concatenated with the optional `request.paramHeaderPrefix`. {{ alert( """All parameters in the query string are [URI Component Encoded](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURIComponent). diff --git a/test/unit/chunked-uploads.js b/test/unit/chunked-uploads.js index 04c3d5ad3..32320afc8 100644 --- a/test/unit/chunked-uploads.js +++ b/test/unit/chunked-uploads.js @@ -21,14 +21,15 @@ if (qqtest.canDownloadFileAsBlob) { function testChunkedUpload(spec) { var customParams = spec.customParams || {}, - chunkingParamNames = spec.chunkingParamNames || new qq.FineUploaderBasic({})._options.chunking.paramNames; + chunkingParamNames = spec.chunkingParamNames || new qq.FineUploaderBasic({})._options.chunking.paramNames, + paramsMode = !!spec.mpe ? qq.paramsMode.paramsInBody : qq.paramsMode.paramsInUrl; assert.expect(3 + (expectedChunks * (20 + (Object.keys(customParams).length))), spec.done); var uploader = new qq.FineUploaderBasic({ request: { endpoint: testUploadEndpoint, - paramsInBody: !!spec.mpe, + paramsMode: paramsMode, forceMultipart: !!spec.mpe, params: customParams }, diff --git a/test/unit/file-upload-params-and-headers.js b/test/unit/file-upload-params-and-headers.js index 7462e3d0e..53ddc424a 100644 --- a/test/unit/file-upload-params-and-headers.js +++ b/test/unit/file-upload-params-and-headers.js @@ -21,7 +21,7 @@ if (qqtest.canDownloadFileAsBlob) { var uploader = new qq.FineUploaderBasic({ request: { endpoint: testUploadEndpoint, - paramsInBody: mpe, + paramsMode: mpe ? qq.paramsMode.paramsInBody : qq.paramsMode.paramsInUrl, forceMultipart: mpe, params: paramsAsOptions ? params : {}, autoUpload: false @@ -32,6 +32,22 @@ if (qqtest.canDownloadFileAsBlob) { return uploader; } + function getSimpleParamsInHeaderUploader(paramsAsOptions, headerParamPrefix) { + var uploader = new qq.FineUploaderBasic({ + request: { + endpoint: testUploadEndpoint, + paramsMode: qq.paramsMode.paramsInHeader, + headerParamPrefix: headerParamPrefix, + forceMultipart: true, + params: paramsAsOptions ? params : {}, + autoUpload: false + } + }); + + !paramsAsOptions && uploader.setParams(params); + return uploader; + } + function getSimpleHeadersUploader(headersAsOptions) { var uploader = new qq.FineUploaderBasic({ request: { @@ -45,16 +61,18 @@ if (qqtest.canDownloadFileAsBlob) { return uploader; } - function assertParamsInRequest(uploader, mpe, done, overrideParams) { + function assertParamsInRequest(uploader, paramsMode, done, overrideParams) { assert.expect(4, done); qqtest.downloadFileAsBlob("up.jpg", "image/jpeg").then(function(blob) { + fileTestHelper.mockXhr(); var request, requestParams, purlUrl, - theparams = overrideParams || params; + theparams = overrideParams || params, + headerParamPrefix = headerParamPrefix || ""; uploader.addFiles({name: "test", blob: blob}); uploader.uploadStoredFiles(); @@ -64,9 +82,21 @@ if (qqtest.canDownloadFileAsBlob) { requestParams = request.requestBody.fields; purlUrl = purl(request.url); - assert.equal(mpe ? requestParams.foo : purlUrl.param("foo"), theparams.foo, "'foo' param value incorrect"); - assert.equal(mpe ? requestParams.one : purlUrl.param("one"), theparams.one, "'one' param value incorrect"); - assert.equal(mpe ? requestParams.thefunc : purlUrl.param("thefunc"), theparams.thefunc(), "'thefunc' param value incorrect"); + if (paramsMode === qq.paramsMode.paramsInBody) { + assert.equal(requestParams.foo, theparams.foo, "'foo' param value incorrect"); + assert.equal(requestParams.one, theparams.one, "'one' param value incorrect"); + assert.equal(requestParams.thefunc, theparams.thefunc(), "'thefunc' param value incorrect"); + } + else if (paramsMode === qq.paramsMode.paramsInUrl) { + assert.equal(purlUrl.param("foo"), theparams.foo, "'foo' param value incorrect"); + assert.equal(purlUrl.param("one"), theparams.one, "'one' param value incorrect"); + assert.equal(purlUrl.param("thefunc"), theparams.thefunc(), "'thefunc' param value incorrect"); + } + else if (paramsMode === qq.paramsMode.paramsInHeader) { + assert.equal(request.requestHeaders.foo, theparams.foo, "'foo' param value incorrect"); + assert.equal(request.requestHeaders.one, theparams.one, "'one' param value incorrect"); + assert.equal(request.requestHeaders.thefunc, theparams.thefunc, "'thefunc' param value incorrect"); + } fileTestHelper.getRequests()[0].respond(200, null, JSON.stringify({success: true})); }); @@ -96,22 +126,52 @@ if (qqtest.canDownloadFileAsBlob) { it("sends correct params in request for MPE uploads w/ params specified as options only", function(done) { var uploader = getSimpleParamsUploader(true, true); - assertParamsInRequest(uploader, true, done); + assertParamsInRequest(uploader, qq.paramsMode.paramsInBody, done); }); it("sends correct params in request for non-MPE uploads w/ params specified as options only", function(done) { var uploader = getSimpleParamsUploader(false, true); - assertParamsInRequest(uploader, false, done); + assertParamsInRequest(uploader, qq.paramsMode.paramsInUrl, done); + }); + + it("sends correct params in header of request for MPE uploads w/ params specified as options only", function(done) { + var uploader = getSimpleParamsInHeaderUploader(true, ""); + assertParamsInRequest(uploader, qq.paramsMode.paramsInHeader, done); + }); + + it("sends correct params in header of request with prefix for MPE uploads w/ params specified as options only", function(done) { + var headerParamPrefix = "x-upload-data-", + uploader = getSimpleParamsInHeaderUploader(true, headerParamPrefix); + + assert.expect(4, done); + + qqtest.downloadFileAsBlob("up.jpg", "image/jpeg").then(function(blob) { + fileTestHelper.mockXhr(); + + var requestHeaders; + + uploader.addFiles({name: "test", blob: blob}); + uploader.uploadStoredFiles(); + + assert.equal(fileTestHelper.getRequests().length, 1, "Wrong # of requests"); + requestHeaders = fileTestHelper.getRequests()[0].requestHeaders; + + assert.equal(requestHeaders[headerParamPrefix + "foo"], params.foo, "'foo' param value incorrect"); + assert.equal(requestHeaders[headerParamPrefix + "one"], params.one, "'one' param value incorrect"); + assert.equal(requestHeaders[headerParamPrefix + "thefunc"], params.thefunc, "'thefunc' param value incorrect"); + + fileTestHelper.getRequests()[0].respond(200, null, JSON.stringify({success: true})); + }); }); it("Sends correct params in request for MPE uploads w/ params specified via API only", function(done) { var uploader = getSimpleParamsUploader(true, false); - assertParamsInRequest(uploader, true, done); + assertParamsInRequest(uploader, qq.paramsMode.paramsInBody, done); }); it("sends correct params in request for non-MPE uploads w/ params specified as options only", function(done) { var uploader = getSimpleParamsUploader(false, false); - assertParamsInRequest(uploader, false, done); + assertParamsInRequest(uploader, qq.paramsMode.paramsInUrl, done); }); it("sends correct params in request for MPE uploads w/ params initially specified via options then overriden via API", function(done) { @@ -119,7 +179,7 @@ if (qqtest.canDownloadFileAsBlob) { overridenParams = qq.extend({one: 3}, params); uploader.setParams(overridenParams); - assertParamsInRequest(uploader, true, done, overridenParams); + assertParamsInRequest(uploader, qq.paramsMode.paramsInBody, done, overridenParams); }); it("sends correct params in request for non-MPE uploads w/ params initially specified via options then overriden via API", function(done) { @@ -127,7 +187,7 @@ if (qqtest.canDownloadFileAsBlob) { overridenParams = qq.extend({foo: "abc"}, params); uploader.setParams(overridenParams); - assertParamsInRequest(uploader, false, done, overridenParams); + assertParamsInRequest(uploader, qq.paramsMode.paramsInUrl, done, overridenParams); }); it("sends correct params in request for MPE uploads when params are overriden via API for specific files", function(done) { @@ -136,7 +196,7 @@ if (qqtest.canDownloadFileAsBlob) { uploader.setParams(overridenParams, 0); uploader.setParams({}, 1); - assertParamsInRequest(uploader, true, done, overridenParams); + assertParamsInRequest(uploader, qq.paramsMode.paramsInBody, done, overridenParams); }); it("sends correct params in request for non-MPE uploads when params are overriden via API for specific files", function(done) { @@ -145,7 +205,7 @@ if (qqtest.canDownloadFileAsBlob) { uploader.setParams(overridenParams, 0); uploader.setParams({}, 1); - assertParamsInRequest(uploader, false, done, overridenParams); + assertParamsInRequest(uploader, qq.paramsMode.paramsInUrl, done, overridenParams); }); it("sends correct headers in request w/ headers specified as options", function(done) { diff --git a/test/unit/simple-file-uploads.js b/test/unit/simple-file-uploads.js index 6da26f4e3..5fa017370 100644 --- a/test/unit/simple-file-uploads.js +++ b/test/unit/simple-file-uploads.js @@ -11,7 +11,7 @@ if (qqtest.canDownloadFileAsBlob) { autoUpload: autoUpload, request: { endpoint: testUploadEndpoint, - paramsInBody: mpe, + paramsMode: mpe ? qq.paramsMode.paramsInBody : qq.paramsMode.paramsInUrl, forceMultipart: mpe }, callbacks: { @@ -478,7 +478,7 @@ if (qqtest.canDownloadFileAsBlob) { uploader.addFiles(canvasWrapper); }); }); - + it("removes reference to a Blob via API", function(done) { qqtest.downloadFileAsBlob("up.jpg", "image/jpeg").then(function(blob) { fileTestHelper.mockXhr();