计算机系统应用教程网站

网站首页 > 技术文章 正文

「React组件封装」「优化」文件选择(安卓file的input不触发onChange)

btikc 2024-10-16 08:17:57 技术文章 6 ℃ 0 评论

[React组件封装][优化]文件选择按钮(解决安卓下type为file的input标签不触发onChange的问题)

详细文章请关注公众号:程序员波波。

或者点击文章最下方了解更多前往原文地址。

之前已经写过“[React组件封装][实例]文件选择按钮组件封装”这篇文章。目的是封装一个文件选择按钮。

但是在真正运用的时候发现,还是存在问题的。主要是安卓端的微信浏览器input标签不触发onChange函数。

在经过一些查阅后,发现可能原因是安卓端的WebView不支持input标签为file,不会触发onChange,而微信安卓端使用了WebView,所以会有这样的问题。

该如何解决呢?

好在微信提供了专门的图片选择接口,这样至少保证FileButton在安卓微信端能够实现图片的选择。

使用微信的jssdk可以在微信浏览器内实现图片选择功能。

使用接口前需要先加载jweixin-1.4.0.js这个文件,然后还需要到服务器端去签名。(服务端具体的签名规则在微信的官方文档中可以查阅到)

然后我这里封装了一个WeixinTools,来完成上面的步骤。

WeixinTools.js:

import Tools from "../Tools";
import Strings from "../StringUtil";
import { UserOperation } from "../UserUtil";
import Http from "../Http";

class WeixinTools {
 static configJsSdk() {
 let arg = {
 cmd: UserOperation.GET_JS_SDK_CONFIG,
 data: {
 url: window.location.href,
 }
 }
 Http.post(Tools.getUrl('/user-operation/'), arg, (data)=>{
 console.log(data)
 if (data.result_id == 0) {
 let ans = data.message
 let js_sdk_config = ans.js_sdk_config
 WeixinTools.jssdk.config({
 debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
 appId: Strings.APP_ID, // 必填,公众号的唯一标识
 timestamp: js_sdk_config.timestamp, // 必填,生成签名的时间戳
 nonceStr: js_sdk_config.noncestr, // 必填,生成签名的随机串
 signature: js_sdk_config.signature,// 必填,签名
 jsApiList: [
 'chooseImage',
 "previewImage",
 "uploadImage",
 "downloadImage",
 "getLocalImgData"
 ] // 必填,需要使用的JS接口列表
 })
 WeixinTools.jssdk.ready(function(){
 console.log('wx js sdk ready')
 // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
 });
 WeixinTools.jssdk.error(function(res){
 console.log('wx js sdk error')
 console.log(res)
 // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
 });
 }
 })
 }
}

var _jssdkDependScripts = [
 "/static/Weixin/jweixin-1.4.0.js",
]

if (Tools.isWeiXin()) {
 Tools.asyncLoadScripts(_jssdkDependScripts, ()=>{
 WeixinTools.jssdk = jWeixin
 })
}

export default WeixinTools

调用 WeixinTools.configJsSdk()这个方法,就可以实现jssdk的初始化。

实际使用:

然后将之前FileButton组件中的代码进行改造,对应安卓微信端使用chooseImage这个接口来实现选择图片。

但是微信选择完图片后回调接口只会返回一个localIds,这个并不是实际的文件,而是一个文件的标识符一样的东西。

通过getLocalImgData这个接口可以使用localId来获取图片的base64数据,不过在安卓端需要手动加上'data:image/jpeg;base64,'前缀。然后通过Tools.jpegBase64ToBlob将base64转化成blob类型,就能统一成FileButton原来的接口了。

Tools.jpegBase64ToBlob方法:

function jpegBase64ToBlob(urlData) {
 try {
 var arr = urlData.split(',')
 var mime = arr[0].match(/:(.*?);/)[1] || 'image/jpeg';
 // 去掉url的头,并转化为byte
 var bytes = window.atob(arr[1]);
 // 处理异常,将ascii码小于0的转换为大于0
 var ab = new ArrayBuffer(bytes.length);
 // 生成视图(直接针对内存):8位无符号整数,长度1个字节
 var ia = new Uint8Array(ab);
 
 for (var i = 0; i < bytes.length; i++) {
 ia[i] = bytes.charCodeAt(i);
 }

 return new Blob([ab], {
 type: mime,
 });
 }
 catch (e) {
 var ab = new ArrayBuffer(0);
 return new Blob([ab], {
 type: 'image/jpeg',
 });
 }
}

最终版FileButton:

import React, { Component } from 'react';
import PropTypes from 'prop-types'
import CSSModules from 'react-css-modules';

import Button from '@material-ui/core/Button';
import Tools from '../../../common_js/Tools'

import styles from './FileButton.css'
import WeixinTools from '../../../common_js/weixin/WeixinTools';

class FileButton extends Component {
 constructor(props) {
 super(props);
 this.button = React.createRef()
 this.getFiles = this.getFiles.bind(this)
 this.onChange = this.onChange.bind(this)
 this.onClick = this.onClick.bind(this)
 }

 click() {
 this.button.current.click()
 }

 onClick() {
 const { onChange } = this.props
 WeixinTools.jssdk.chooseImage({
 count: 1, // 默认9
 sizeType: ['original'], // 可以指定是原图还是压缩图,默认二者都有
 sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
 success: function (res) {
 let localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
 WeixinTools.jssdk.getLocalImgData({
 localId: localIds[0],
 success: function (res) {
 let localData = 'data:image/jpeg;base64,' + res.localData
 // let localData = 'data:image/jpeg;base64,' + res.localData.replace(/\r|\n/g, '');
 if (onChange) {
 onChange([Tools.jpegBase64ToBlob(localData)])
 }
 }
 });
 }
 });
 }

 getFiles() {
 let ans = []
 let files = this.refs.fileLoader.files
 for (let i = 0; i < files.length; i++) {
 ans.push(files[i])
 }
 return ans
 }

 onChange(e) {
 if (!Tools.isNone(this.props.onChange)) {
 this.props.onChange(this.getFiles())
 }
 this.refs.fileLoader.value = ''
 }

 componentDidMount() {
 if (Tools.isWeiXin() && Tools.isAndroid()) {
 WeixinTools.configJsSdk()
 }
 }

 render() {
 let { accept, onChange, children, multiple, ...other } = this.props
 if (Tools.isNone(accept)) {
 accept = 'image/*'
 if (Tools.isChrome()) {
 accept = 'image/jpeg,image/gif,image/png,image/bmp'
 }
 }

 if (Tools.isWeiXin() && Tools.isAndroid()) {
 return (
 
 );
 }

 return (
 
 );
 }
}

FileButton.propTypes = {
 onChange: PropTypes.func,
 accept: PropTypes.string,
}

export default CSSModules(FileButton, styles);

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表