如何将Base64字符串转换为JavaScript文件对象,就像从文件输入表单中一样?

171

我想将从文件中提取的Base64字符串(例如:"AAAAA....~")转换为JavaScript文件对象。

我指的JavaScript文件对象就像这段代码:

HTML:

<input type="file" id="selectFile" > 

JS:

$('#selectFile').on('change', function(e) {
  var file = e.target.files[0];

  console.log(file)
}

'file'变量是JavaScript文件对象。因此,我想将base64字符串转换为JavaScript文件对象。

我只想通过解码base64字符串(由其他应用程序从文件编码而成)而获取文件对象,而无需使用HTML文件输入表单。

谢谢。

10个回答

320

方法一:仅适用于DataURL,不适用于其他类型的URL。

function dataURLtoFile(dataurl, filename) {
    var arr = dataurl.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[arr.length - 1]), 
        n = bstr.length, 
        u8arr = new Uint8Array(n);
    while(n--){
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], filename, {type:mime});
}

//Usage example:
var file = dataURLtoFile('data:text/plain;base64,aGVsbG8=','hello.txt');
console.log(file);

方法二:适用于任何类型的URL(http URL,dataURL,blobURL等)

// return a promise that resolves with a File instance
function urltoFile(url, filename, mimeType){
    if (url.startsWith('data:')) {
        var arr = url.split(','),
            mime = arr[0].match(/:(.*?);/)[1],
            bstr = atob(arr[arr.length - 1]), 
            n = bstr.length, 
            u8arr = new Uint8Array(n);
        while(n--){
            u8arr[n] = bstr.charCodeAt(n);
        }
        var file = new File([u8arr], filename, {type:mime || mimeType});
        return Promise.resolve(file);
    }
    return fetch(url)
        .then(res => res.arrayBuffer())
        .then(buf => new File([buf], filename,{type:mimeType}));
}

//Usage example:
urltoFile('data:text/plain;base64,aGVsbG8=', 'hello.txt','text/plain')
.then(function(file){ console.log(file);});


7
警告:在我看来,File对象仅在Firefox上可用。建议使用Blob对象:https://developer.mozilla.org/en-US/docs/Web/API/Blob - TeChn4K
在方式1中,我得到的是[object Object]而不是[object file]。我该如何获得[object file]?@cuixiping - Edukondalu Thaviti
@TeChn4K 在不支持File构造函数的旧浏览器中,请使用Blob代替File。 - cuixiping
@EdukondaluThaviti 你可以将你的代码粘贴到 jsfiddle 上,并将链接发给我。 - cuixiping
1
这不适用于格式为data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQA的文件。您能否告诉我这是否可行?@cuixiping - coderpc
显示剩余9条评论

109
const url = 'data:image/png;base6....';
fetch(url)
  .then(res => res.blob())
  .then(blob => {
    const file = new File([blob], "File name",{ type: "image/png" })
  })

Base64字符串 -> Blob -> 文件。


10
注意:这与大多数内容安全策略不兼容,如果您有使用其中之一,请注意。 - MaxGabriel
@MaxGabriel 你可以详细说明一下吗? - Rambatino
2
如果您有一个CSP,其中包含connect-src属性以允许JavaScript加载哪些URL,则它将拒绝并显示以下错误:“Refused to connect to 'data:text/plain;base64,aGVsbG8gd29ybGQ=' because it violates the following Content Security Policy directive: "connect-src <...>". 您可以在CSP中将数据URL列入白名单,但MDN警告说:“这是不安全的;攻击者也可以注入任意数据:URI。请谨慎使用,绝对不要用于脚本。” https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src - MaxGabriel

66

这是最新的 async/await 模式解决方案。

export async function dataUrlToFile(dataUrl: string, fileName: string): Promise<File> {

    const res: Response = await fetch(dataUrl);
    const blob: Blob = await res.blob();
    return new File([blob], fileName, { type: 'image/png' });
}

4
我会尽力为您翻译:我给这个评分,因为这是对我最好的方法。此外,我使用了Typescript。 - rottitime
你可以从base64 url中获取文件类型:dataUrl.match(/^data:(.+);base64/)?.[1] - Eugene P.
请勿编写“Response”和“Blob”类型。TS将推断它们。 - Eugene P.
1
对我有效的唯一答案。在Vue JS中,我使用了以下代码:async dataUrlToFile(dataUrl, fileName){ let res = await fetch(dataUrl) const blob = await res.blob() return new File([blob], fileName, { type: 'image/png' }) } - undefined

6

以下是@cuixiping提供的答案的TypeScript版本,现在使用Buffer而不是atob()。

我在使用TypeScript中的atob()时看到了过时警告,尽管它并没有完全过时。只有一个重载已经过时。然而,我将我的代码转换为使用缺省警告建议的Buffer。这种方法似乎更加简洁,因为它不需要额外的循环来转换每个字符。

  /***
   * Converts a dataUrl base64 image string into a File byte array
   * dataUrl example:
   * data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIsAAACLCAYAAABRGWr/AAAAAXNSR0IA...etc
   */
  dataUrlToFile(dataUrl: string, filename: string): File | undefined {
    const arr = dataUrl.split(',');
    if (arr.length < 2) { return undefined; }
    const mimeArr = arr[0].match(/:(.*?);/);
    if (!mimeArr || mimeArr.length < 2) { return undefined; }
    const mime = mimeArr[1];
    const buff = Buffer.from(arr[1], 'base64');
    return new File([buff], filename, {type:mime});
  }

在文件顶部,您需要导入以使类型匹配。

import { Buffer } from 'buffer';

不需要特殊的npm包。


仍然在2023年9月13日运作得很好。谢谢! 如果您已经有解析后的base64字符串,您可以直接使用以下代码: const buff = Buffer.from(yourBase64StringContent, "base64"); const fetchedFile = new File([buff], yourDesiredFileName); - undefined

4
const file = new File([
  new Blob(["decoded_base64_String"])
], "output_file_name");

您可以使用像这个库来将base64编码解码为arrayBuffer,并将arrayBuffer编码成base64。

2
它对我没用! - Hamid
1
适用于浏览器(谢谢),但不适用于React Native(错误:不支持从“ArrayBuffer”和“ArrayBufferView”创建Blob)。 - Ammar Faris
对于React Native,我使用表单数据方法。 - Ammar Faris

3
我有一个非常类似的需求(从外部xml导入文件中导入base64编码的图像)。在使用 xml2json-light库将其转换为JSON对象后,我能够利用cuixiping上面的答案中的见解将传入的b64编码图像转换为文件对象。
const imgName = incomingImage['FileName'];
const imgExt = imgName.split('.').pop();
let mimeType = 'image/png';
if (imgExt.toLowerCase() !== 'png') {
    mimeType = 'image/jpeg';
}
const imgB64 = incomingImage['_@ttribute'];
const bstr = atob(imgB64);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
  u8arr[n] = bstr.charCodeAt(n);
}
const file = new File([u8arr], imgName, {type: mimeType});

我的json对象经过xml2json-light转换后有两个属性:FileName和_@ttribute(这是包含在传入元素主体中的b64图像数据)。我需要根据传入的FileName扩展名生成mime类型。一旦我从json对象中提取/引用了所有的部分,使用cuixiping提供的代码参考就可以很容易地生成新的File对象,这个对象与我现有的期望从浏览器元素生成的文件对象完全兼容。
希望这能帮助其他人理解。

0

注意:

JAVASCRIPT

<script>
   function readMtlAtClient(){

       mtlFileContent = '';

       var mtlFile = document.getElementById('mtlFileInput').files[0];
       var readerMTL = new FileReader();

       // Closure to capture the file information.
       readerMTL.onload = (function(reader) {
           return function() {
               mtlFileContent = reader.result;
               mtlFileContent = mtlFileContent.replace('data:;base64,', '');
               mtlFileContent = window.atob(mtlFileContent);

           };
       })(readerMTL);

       readerMTL.readAsDataURL(mtlFile);
   }
</script>

HTML

    <input class="FullWidth" type="file" name="mtlFileInput" value="" id="mtlFileInput" 
onchange="readMtlAtClient()" accept=".mtl"/>

那么 mtlFileContent 就包含了你的文本解码后的字符串!


0

Typescript的完整版本

async uploadImage(b64img: string) {
  var file = await this.urltoFile(b64img,'name.png',this.base64MimeType(b64img));
}

//return a promise that resolves with a File instance
urltoFile(url, filename, mimeType){
    return (fetch(url)
        .then(function(res){return res.arrayBuffer();})
        .then(function(buf){return new File([buf], filename,{type:mimeType});})
    );
}

//return mime Type of bs64
base64MimeType(encoded) {
    var result = null;
  
    if (typeof encoded !== 'string') {
      return result;
    }
  
    var mime = encoded.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/);
  
    if (mime && mime.length) {
      result = mime[1];
    }
  
    return result;
}

这对我有用,将base64图像转换为文件


0

这对我有用 信用

base64ToFile = (url: string) => {
let arr = url.split(',');
// console.log(arr)
let mime = arr[0].match(/:(.*?);/)![1];
let data = arr[1];

let dataStr = atob(data);
let n = dataStr.length;
let dataArr = new Uint8Array(n);

while (n--) {
  dataArr[n] = dataStr.charCodeAt(n);
}

let file = new File([dataArr], 'File.png', { type: mime });

return file;
};

0
const base64Str = "AAABAAEAICAAAAAAAACoCAAAFg....AND_SO_ON";
const file = new File(
   [Uint8Array.from(atob(base64Str), (m) => m.codePointAt(0))],
   'myfilename.jpeg',
   { type: "image/jpeg" }
);

这需要一些解释为什么它有效的背景。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接