diff --git a/docs/api/media/image.md b/docs/api/media/image.md index 55492be0ffc1a22be92f19dca9f64648d5716def..217e5965ef32c26e3d88ef740d5cedfbbfe521eb 100644 --- a/docs/api/media/image.md +++ b/docs/api/media/image.md @@ -29,7 +29,7 @@ App端如需要更丰富的相机拍照API(如直接调用前置摄像头) |参数|类型|说明| |:-|:-|:-| |tempFilePaths|Array<String>|图片的本地文件路径列表| -|tempFiles|Array<Object>|图片的本地文件列表,每一项是一个 File 对象| +|tempFiles|Array<Object>、Array<File>|图片的本地文件列表,每一项是一个 File 对象| **File 对象结构如下** @@ -38,6 +38,7 @@ App端如需要更丰富的相机拍照API(如直接调用前置摄像头) |path|String|本地文件路径| |size|Number|本地文件大小,单位:B| |name|String|包含扩展名的文件名称,仅H5支持| +|type|String|文件类型,仅H5支持| **示例** diff --git a/docs/api/media/video.md b/docs/api/media/video.md index 990c7fad980eaafc07e42a022aca8580bf6c1fb2..8b50c95f0ec6f57a3cc6c0124eda0595a512b1fb 100644 --- a/docs/api/media/video.md +++ b/docs/api/media/video.md @@ -21,14 +21,15 @@ **success 返回参数说明** -|参数|说明|平台差异说明说明| -|:-|:-|:-| -|tempFilePath|选定视频的临时文件路径|| -|duration|选定视频的时间长度,单位为 s|APP平台 2.1.0+、微信小程序| -|size|选定视频的数据量大小|APP平台 2.1.0+、微信小程序| -|height|返回选定视频的高|APP平台 2.1.0+、微信小程序| -|width|返回选定视频的宽|APP平台 2.1.0+、微信小程序| -|name|包含扩展名的文件名称|仅H5支持| +|参数|类型|说明|平台差异说明说明| +|:-|:-|:-|:-| +|tempFilePath|String|选定视频的临时文件路径|| +|tempFile|File|选定的视频文件|仅H5(2.7.0+)支持| +|duration|Number|选定视频的时间长度,单位为 s|APP 2.1.0+、H5、微信小程序| +|size|Number|选定视频的数据量大小|APP 2.1.0+、H5、微信小程序| +|height|Number|返回选定视频的高|APP 2.1.0+、H5、微信小程序| +|width|Number|返回选定视频的宽|APP 2.1.0+、H5、微信小程序| +|name|String|包含扩展名的文件名称|仅H5支持| **注意:** * 文件的临时路径,在应用本次启动期间可以正常使用,如需持久保存,需在主动调用 [uni.saveFile](api/file/file?id=savefile),在应用下次启动时才能访问得到。 diff --git a/docs/api/request/network-file.md b/docs/api/request/network-file.md index d272f87607ef7b052d6c290d3e3e028a9c83c926..aaf98cb34ac3df2268918c238cb90fabe2b9d5f9 100644 --- a/docs/api/request/network-file.md +++ b/docs/api/request/network-file.md @@ -9,8 +9,9 @@ |参数名|类型|必填|说明|平台差异说明| |:-|:-|:-|:-|:-| |url|String|是|开发者服务器 url|| -|files|Array|否|需要上传的文件列表。**使用 files 时,filePath 和 name 不生效。**|App| +|files|Array|否|需要上传的文件列表。**使用 files 时,filePath 和 name 不生效。**|App、H5( 2.7.0+)| |fileType|String|见平台差异说明|文件类型,image/video/audio|仅支付宝小程序,且必填。| +|file|File|否|要上传的文件对象。|仅H5(2.7.0+)支持| |filePath|String|是|要上传文件资源的路径。|| |name|String|是|文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容|| |header|Object|否|HTTP 请求 Header, header 中不能设置 Referer。|| @@ -33,6 +34,7 @@ files 参数是一个 file 对象的数组,file 对象的结构如下: |参数名|类型|必填|说明| |:-|:-|:-|:-| |name|String|否|multipart 提交时,表单的项目名,默认为 file| +|file|File|否|要上传的文件对象,仅H5(2.7.0+)支持| |uri|String|是|文件的本地地址| Tip: diff --git a/docs/api/request/request.md b/docs/api/request/request.md index c5855b95c7a2c28524f0ad8b3544509cfe1409d3..c667dbac0aed78cf2f2b62273376ca6602769d74 100644 --- a/docs/api/request/request.md +++ b/docs/api/request/request.md @@ -15,6 +15,7 @@ |dataType|String|否|json |如果设为 json,会尝试对返回的数据做一次 JSON.parse|| |responseType|String|否|text |设置响应的数据类型。合法值:text、arraybuffer|App和支付宝小程序不支持| |sslVerify|Boolean|否|true|验证 ssl 证书|仅App安卓端支持(HBuilderX 2.3.3+)| +|withCredentials|Boolean|否|false|跨域请求时是否携带凭证(cookies)|仅H5支持(HBuilderX 2.7.0+)| |success|Function|否||收到开发者服务成功返回的回调函数|| |fail|Function|否||接口调用失败的回调函数|| |complete|Function|否||接口调用结束的回调函数(调用成功、失败都会执行)| | diff --git a/src/core/helpers/protocol/network/request.js b/src/core/helpers/protocol/network/request.js index 76286be30762d3e2de11e8a5016a3c77e8e0c370..b119a255000f51045ddd5d85de1b024e0f570256 100644 --- a/src/core/helpers/protocol/network/request.js +++ b/src/core/helpers/protocol/network/request.js @@ -100,5 +100,8 @@ export const request = { value = (value || '').toLowerCase() params.responseType = Object.values(responseType).indexOf(value) < 0 ? responseType.TEXT : value } + }, + withCredentials: { + type: Boolean } -} +} diff --git a/src/core/helpers/protocol/network/upload-file.js b/src/core/helpers/protocol/network/upload-file.js index 47cdd37659ffd0869082c2850837aa94e9471449..bcf582eb7d3c47917f5cd9023ad0b0b10ebc2e36 100644 --- a/src/core/helpers/protocol/network/upload-file.js +++ b/src/core/helpers/protocol/network/upload-file.js @@ -9,6 +9,9 @@ export const uploadFile = { files: { type: Array }, + file: { + type: File + }, filePath: { type: String, validator (value, params) { diff --git a/src/platforms/h5/helpers/file.js b/src/platforms/h5/helpers/file.js index 3a9a82a6b39611c8b6c3072c4e6c44d6445c23da..c6337cdd552ae047d9d7037442bff986375b2985 100644 --- a/src/platforms/h5/helpers/file.js +++ b/src/platforms/h5/helpers/file.js @@ -61,3 +61,8 @@ export function fileToUrl (file) { files[url] = file return url } + +export function revokeObjectURL (url) { + (window.URL || window.webkitURL).revokeObjectURL(url) + delete files[url] +} diff --git a/src/platforms/h5/service/api/media/choose-image.js b/src/platforms/h5/service/api/media/choose-image.js index e3c09b89c46d1fb497405711406438918eaa054d..927b360ecf40034bc6d963c930874375e84e5b11 100644 --- a/src/platforms/h5/service/api/media/choose-image.js +++ b/src/platforms/h5/service/api/media/choose-image.js @@ -50,26 +50,27 @@ export function chooseImage ({ document.body.appendChild(imageInput) imageInput.addEventListener('change', function (event) { - const tempFilePaths = [] const tempFiles = [] const fileCount = event.target.files.length for (let i = 0; i < fileCount; i++) { const file = event.target.files[i] - const filePath = fileToUrl(file) - - tempFilePaths.push(filePath) - tempFiles.push({ - path: filePath, - size: file.size, - name: file.name + let filePath + Object.defineProperty(file, 'filePath', { + get () { + filePath = filePath || fileToUrl(file) + return filePath + } }) + tempFiles.push(file) } - - invoke(callbackId, { + const res = { errMsg: 'chooseImage:ok', - tempFilePaths: tempFilePaths, + get tempFilePaths () { + return tempFiles.map(({ filePath }) => filePath) + }, tempFiles: tempFiles - }) + } + invoke(callbackId, res) // TODO 用户取消选择时,触发 fail,目前尚未找到合适的方法。 }) diff --git a/src/platforms/h5/service/api/media/choose-video.js b/src/platforms/h5/service/api/media/choose-video.js index 65258be5964f2f03c0edef239b55b86a72ab8ccd..7dc0ccdaee4a90e297100bc0bb7b0db043851eb4 100644 --- a/src/platforms/h5/service/api/media/choose-video.js +++ b/src/platforms/h5/service/api/media/choose-video.js @@ -1,4 +1,4 @@ -import { fileToUrl } from 'uni-platform/helpers/file' +import { fileToUrl, revokeObjectURL } from 'uni-platform/helpers/file' import { updateElementStyle } from 'uni-shared' const { @@ -42,23 +42,30 @@ export function chooseVideo ({ videoInput.addEventListener('change', function (event) { const file = event.target.files[0] - const filePath = fileToUrl(file) - - let callbackResult = { + const callbackResult = { errMsg: 'chooseVideo:ok', - tempFilePath: filePath, + tempFile: file, size: file.size, duration: 0, width: 0, height: 0, name: file.name } + let filePath + Object.defineProperty(callbackResult, 'tempFilePath', { + get () { + filePath = filePath || fileToUrl(this.tempFile) + return filePath + } + }) const video = document.createElement('video') if (video.onloadedmetadata !== undefined) { + const filePath = fileToUrl(file) // 尝试获取视频的宽高信息 video.onloadedmetadata = function () { - invoke(callbackId, Object.assign({}, callbackResult, { + revokeObjectURL(filePath) + invoke(callbackId, Object.assign(callbackResult, { duration: video.duration || 0, width: video.videoWidth || 0, height: video.videoHeight || 0 @@ -66,11 +73,9 @@ export function chooseVideo ({ } // 部分浏览器(如微信内置浏览器)未播放无法触发loadedmetadata事件 setTimeout(() => { - invoke(callbackId, Object.assign({}, callbackResult, { - duration: 0, - width: 0, - height: 0 - })) + video.onloadedmetadata = null + revokeObjectURL(filePath) + invoke(callbackId, callbackResult) }, 300) video.src = filePath } else { diff --git a/src/platforms/h5/service/api/network/request.js b/src/platforms/h5/service/api/network/request.js index 4eee528465f7a5f4bd34ca33cb7852dbfb0441a8..f147d7e1a3672ca0684de4c19f75784e9500364b 100644 --- a/src/platforms/h5/service/api/network/request.js +++ b/src/platforms/h5/service/api/network/request.js @@ -45,7 +45,8 @@ export function request ({ header, method, dataType, - responseType + responseType, + withCredentials }, callbackId) { const { invokeCallbackHandler: invoke @@ -143,6 +144,7 @@ export function request ({ errMsg: 'request:fail' }) } + xhr.withCredentials = withCredentials xhr.send(body) return requestTask } diff --git a/src/platforms/h5/service/api/network/upload-file.js b/src/platforms/h5/service/api/network/upload-file.js index 1e9db34e52ccc5a5b91b1ed99798ca103aaad6ba..15b97051a63978bfa30419487d2414733b0874ae 100644 --- a/src/platforms/h5/service/api/network/upload-file.js +++ b/src/platforms/h5/service/api/network/upload-file.js @@ -45,8 +45,10 @@ class UploadTask { */ export function uploadFile ({ url, + file, filePath, name, + files, header, formData }, callbackId) { @@ -55,15 +57,24 @@ export function uploadFile ({ invokeCallbackHandler: invoke } = UniServiceJSBridge var uploadTask = new UploadTask(null, callbackId) - - function upload (file) { + if (!Array.isArray(files) || !files.length) { + files = [{ + name, + file, + uri: filePath + }] + } + function upload (realFiles) { var xhr = new XMLHttpRequest() var form = new FormData() var timer Object.keys(formData).forEach(key => { form.append(key, formData[key]) }) - form.append(name, file, file.name || `file-${Date.now()}`) + Object.values(files).forEach(({ name }, index) => { + const file = realFiles[index] + form.append(name || 'file', file, file.name || `file-${Date.now()}`) + }) xhr.open('POST', url) Object.keys(header).forEach(key => { xhr.setRequestHeader(key, header[key]) @@ -118,13 +129,16 @@ export function uploadFile ({ } } - urlToFile(filePath).then(upload).catch(() => { - setTimeout(() => { - invoke(callbackId, { - errMsg: 'uploadFile:fail file error' - }) - }, 0) - }) + Promise + .all(files.map(({ file, uri }) => file instanceof File ? Promise.resolve(file) : urlToFile(uri))) + .then(upload) + .catch(() => { + setTimeout(() => { + invoke(callbackId, { + errMsg: 'uploadFile:fail file error' + }) + }, 0) + }) return uploadTask }