祈愿稻荷神
祈愿稻荷神
  • 发布:2024-01-15 15:31
  • 更新:2024-01-15 15:31
  • 阅读:208

在base64转本地文件(如pdf,apk,音频等)如果base64长度过长 在 安卓中 Base64.decode(str, 0) 返回 null 的解决问题

分类:uni-app

在APP端中我们有一些需求,比如我 使用 webview 展现的内容需要生成PDF,中间省略过程,最终iframe将 PDF 的base64 传给了APP。
但是在安卓APP中使用:

var Base64 = plus.android.importClass("android.util.Base64");  
var bytes = Base64.decode(base64Str, Base64.DEFAULT);  

如果base64长度过长会导致最终返回的 bytes 为 null
在翻查过往经验中也发现了有人出现这个问题,原因就是 因为 NativeJS 使用会有参数长度限制
问题摘自 :https://ask.dcloud.net.cn/question/93515

评论下有说使用 腾讯X5内核解决问题,但是我试了依旧不行。(可能是我使用姿势不对吧)
然后我试了最后一层大佬的想法分段字符串获取 bytes 最后合并到一起,很高兴这样也是没问题,但是因为使用 NativeJS 有性能传输损耗,实际情况导致需要加载很久。(10M的PDF 生成实际大概需要4分钟)

没办法了,看了下数据实际最后返回就是带符号的整数数组,查阅资料手动写个普通方法转(因为对原生也不熟)用于替换 Base64.decode 方法

function base64ToByteArray(base64Str) {  
    const binaryString = atob(base64Str);  
    const uint8Array = new Uint8Array(binaryString.length);  

    for (let i = 0; i < binaryString.length; i++) {  
        uint8Array[i] = binaryString.charCodeAt(i);  
    }  
    let arr = []  
    Array.from(uint8Array).map(num => {  
        arr.push(num >= 128 ? (num - 256) : num)  
    })  
    return arr;  
}

最终的 base64转本地文件 方法也就出现了

/**  
 * base64字符串转成文件  
 * @param {String} base64Str // 允许包含前缀  
 * @param {String} fileName // 文件名称:1663061363470.xlsx  
 * @param {Object} callback  // 返回本地路径径URL,file:///xxx/doc/1663062980631.xlsx  
 */  
function base64ToFile(base64Str, fileName, callback) {  
    // #ifdef H5  
    // 去除base64前缀  
    var index = base64Str.indexOf(',');  
    base64Str = base64Str.slice(index + 1, base64Str.length);  

    // 创建Blob对象  
    var blob = dataURItoBlob(base64Str);  

    // 创建一个新的Blob URL  
    var blobUrl = window.URL.createObjectURL(blob);  

    // 创建一个新的a标签用于下载  
    var a = document.createElement('a');  
    a.href = blobUrl;  
    a.download = fileName;  
    document.body.appendChild(a);  

    // 模拟点击a标签触发下载  
    a.click();  

    // 移除a标签和Blob URL  
    document.body.removeChild(a);  
    window.URL.revokeObjectURL(blobUrl);  

    // 回调  
    callback && callback(blobUrl);  

    // 将base64字符串转换为Blob对象  
    function dataURItoBlob(dataURI) {  
        var byteString = atob(dataURI);  
        var arrayBuffer = new ArrayBuffer(byteString.length);  
        var uint8Array = new Uint8Array(arrayBuffer);  
        for (var i = 0; i < byteString.length; i++) {  
            uint8Array[i] = byteString.charCodeAt(i);  
        }  
        return new Blob([uint8Array], {  
            type: 'application/octet-stream'  
        });  
    }  
    // #endif  
    // #ifdef APP-PLUS  
    // 去除base64前缀  
    var index = base64Str.indexOf(',')  
    var base64Str = base64Str.slice(index + 1, base64Str.length)  

    plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {  
        fs.root.getFile(fileName, {  
            create: true  
        }, function(entry) {  
            // 获得本地路径URL,file:///xxx/doc/1663062980631.xlsx  
            var fullPath = entry.fullPath;  
            let platform = uni.getSystemInfoSync().platform;  
            if (platform == 'android') {  
                var FileOutputStream = plus.android.importClass("java.io.FileOutputStream");  
                try {  
                    function base64ToByteArray(base64Str) {  
                        const binaryString = atob(base64Str);  
                        const uint8Array = new Uint8Array(binaryString.length);  

                        for (let i = 0; i < binaryString.length; i++) {  
                            uint8Array[i] = binaryString.charCodeAt(i);  
                        }  
                        let arr = []  
                        Array.from(uint8Array).map(num => {  
                            arr.push(num >= 128 ? (num - 256) : num)  
                        })  
                        return arr;  
                    }  
                    var out = new FileOutputStream(fullPath);  
                    let bytes = base64ToByteArray(base64Str);  
                    if (bytes == null || bytes.length == 0) {  
                        out.close();  
                        uni.hideLoading();  
                        uni.showModal({  
                            title: "生成失败",  
                            content: "nativeJS限制参数长度无法获取文件!",  
                            showCancel: false  
                        })  
                        return  
                    } else {  
                        out.write(bytes);  
                        out.close();  
                        // 回调    
                        callback && callback(entry.toLocalURL());  
                        return  
                    }  
                } catch (e) {  
                    console.log(e.message);  
                }  
            } else if (platform == 'ios') {  
                var NSData = plus.ios.importClass('NSData');  
                var nsData = new NSData();  
                nsData = nsData.initWithBase64EncodedStringoptions(base64Str, 0);  
                if (nsData) {  
                    nsData.plusCallMethod({  
                        writeToFile: fullPath,  
                        atomically: true  
                    });  
                    plus.ios.deleteObject(nsData);  
                }  
                // 回调    
                callback && callback(entry.toLocalURL());  
            }  
        })  
    })  
    // #endif  
}  

//使用参考  
base64ToFile(base64Str, "123.pdf", (path) => {  
    uni.hideLoading();  
    console.log("path=>", path);  
    // #ifdef APP  
    uni.openDocument({  
        filePath: path,  
        showMenu: true,  
        success: function(res) {  
            console.log('打开文档成功');  
        },  
        fail: (err) => {  
            console.log(err)  
            this.showSPModal("提示", "文件打开失败!" + err.errMsg)  
        }  
    });  
    // #endif  
})

好了,上面就是记录我base64转文件至本地的经验了,避免下一个人踩坑。

0 关注 分享

要回复文章请先登录注册