提交 63daa2a9 编写于 作者: Q qiang

Merge branch 'next' into dev

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