从输入表单获取Base64编码的文件数据

119

我有一个基本的HTML表单,可以从中获取一些信息,我正在使用Firebug进行检查。

我的唯一问题是,在将文件数据发送到服务器之前,我正在尝试对其进行base64编码。在那里,数据必须以这种形式保存到数据库。

<input type="file" id="fileupload" />

在 JavaScript+jQuery 中:

var file = $('#fileupload').attr("files")[0];

我基于现有的JavaScript进行了一些操作:.getAsBinary()、.getAsText()、.getAsTextURL

然而,这些操作都无法返回可用的文本,因为它们包含不可用的“字符” - 我不想在上传文件时出现“后台提交(postback)”,并且我需要将多个表单指向特定的对象,所以通过获取文件并使用JavaScript是很重要的。

我该如何获取文件以便可以使用广泛可用的JavaScript base64编码器之一呢?

谢谢

更新-在此开始悬赏,需要跨浏览器支持!!!

下面是我的进展:

<input type="file" id="fileuploadform" />

<script type="text/javascript">
var uploadformid = 'fileuploadform';
var uploadform = document.getElementById(uploadformid);


/* method to fetch and encode specific file here based on different browsers */

</script>

跨浏览器支持存在几个问题:

var file = $j(fileUpload.toString()).attr('files')[0];
fileBody = file.getAsDataURL(); // only would works in Firefox

另外,IE不支持:

var file = $j(fileUpload.toString()).attr('files')[0];

所以我必须替换为:

var element = 'id';
var element = document.getElementById(id);

针对IE浏览器的支持。

这在Firefox、Chrome和Safari中有效(但不会正确编码文件,或者至少在发布后文件无法正确显示)。

var file = $j(fileUpload.toString()).attr('files')[0];
var encoded = Btoa(file);

此外,

file.readAsArrayBuffer() 

看起来只支持HTML5?

很多人建议: http://www.webtoolkit.info/javascript-base64.html

但是在进行base64编码之前,UTF_8方法会返回错误(或空字符串)。

var encoded = Base64.encode(file); 

你正在上传什么?是文本数据还是二进制数据? - beatgammit
@tjamenson 二进制数据——任何东西都可以,比如“Word文档”、“PDF”、“图像”等。我原本打算将文件名分配给另一个变量,但我开始怀疑自己对上传表单和HTML的可能性是否有致命缺陷——这种方法完全不可行,无法通过将文件数据作为字符串进行发布传输吗?此外,没有HTML5,我正在阅读一些解决方案。 - jordan.baucke
有趣。为什么你要在浏览器和服务器之间进行base-64编码,并试图在浏览器中完成这个任务?如果你想保护数据,似乎SSL会更容易,因为它可以加密所有HTTP数据(包括文件)通过网络传输,而且如果需要的话,你可以在服务器端进行base-64编码以便存储到数据库中。 - William Walseth
@William 我不是在试图保护数据,只是想将其转换为正确的格式。Salesforce.com(Apex平台)要求在记录创建到数据库之前对数据库进行base64编码,他们通过表单对文件数据进行_base64编码的功能_非常缺乏文档支持_(我在问了这个问题后找到了如何做到这一点)。无论如何,看起来只有HTML5Mozilla File API原生支持对二进制文件数据进行base64编码。 - jordan.baucke
1
好的,现在我明白你为什么需要这个了。没有中间服务器+疯狂的云需求+没有跨浏览器解决方案=一团糟。抱歉。 - William Walseth
8个回答

144

完全可以使用浏览器端的javascript实现。

简单方法:

readAsDataURL() 方法可能已经为您编码成了base64。您可能需要删除开始部分(到第一个,),但这没有太大的问题。这会让所有的乐趣消失。

困难方法:

如果您想尝试困难方法(或者这个方法不起作用),请查看readAsArrayBuffer()。这将为您提供一个Uint8Array,您可以使用指定的方法。这可能只有在您想要修改数据本身,例如操作图像数据或在上传之前进行其他神秘魔法时才有用。

有两种方法:

  • 转换为字符串并使用内置的btoa或类似方法
    • 我没有测试所有情况,但对我有效-只需获取字符代码
  • 直接从Uint8Array转换为base64

我最近在浏览器中实现了tar。在这个过程中,我制作了自己的直接Uint8Array->base64实现。我认为您不需要那个,但是如果您想要查看,它在这里,非常棒。

我现在做什么:

将Uint8Array转换为字符串的代码非常简单(其中buf是一个Uint8Array):
function uint8ToString(buf) {
    var i, length, out = '';
    for (i = 0, length = buf.length; i < length; i += 1) {
        out += String.fromCharCode(buf[i]);
    }
    return out;
}

从那里开始,只需执行以下操作:

var base64 = btoa(uint8ToString(yourUint8Array));

现在,Base64将成为一个Base64编码的字符串,并且应该上传成功。如果您想在推送之前进行双重检查,请尝试以下操作:

window.open("data:application/octet-stream;base64," + base64);

这将以文件形式下载它。

其他信息:

要将数据作为Uint8Array获取,请查看MDN文档:


太好了 - 我以为 readAsArrayBuffer() 输出的是看起来像正确的 base64 数据,但由于字符串的开头部分无法处理 -- 现在我会尝试一下! - jordan.baucke
1
readAsArrayBuffer() 在 Chrome/Safari 中似乎不存在,我认为在其他浏览器中也可能会出现问题 ~ 是否有相应的替代方法可以在这些其他浏览器中使用? - jordan.baucke
createObjectURL() <-- 这看起来很有前途,我会尝试实现它,但我确定IE需要第三方支持。 - jordan.baucke
在执行下载解决方案之前,是否可以指定文件名? - Ωmega
@Ωmega - 不行。这只是下载一个二进制流。没有办法(据我所知)指定一个文件名。一些浏览器会猜测一个扩展名。要获取文件名,你需要先上传然后再下载。 - beatgammit

64

我的解决方案是使用readAsBinaryString()并对其结果使用btoa()

uploadFileToServer(event) {
    var file = event.srcElement.files[0];
    console.log(file);
    var reader = new FileReader();
    reader.readAsBinaryString(file);

    reader.onload = function() {
        console.log(btoa(reader.result));
    };
    reader.onerror = function() {
        console.log('there are some problems');
    };
}

4
迄今为止最简单的方法,为什么没有更多的投票? - Kabir Sarin
在提交表单之前,我仍然需要将btoa(reader.result)的值分配给变量并附加到表单上。 - Wisarut Bholsithi

15

我使用了FileReader来在文件上传按钮的点击事件中显示图片,而不使用任何Ajax请求。以下是代码,希望可以对某些人有所帮助。

$(document).ready(function($) {
    $.extend( true, jQuery.fn, {        
        imagePreview: function( options ){          
            var defaults = {};
            if( options ){
                $.extend( true, defaults, options );
            }
            $.each( this, function(){
                var $this = $( this );              
                $this.bind( 'change', function( evt ){

                    var files = evt.target.files; // FileList object
                    // Loop through the FileList and render image files as thumbnails.
                    for (var i = 0, f; f = files[i]; i++) {
                        // Only process image files.
                        if (!f.type.match('image.*')) {
                        continue;
                        }
                        var reader = new FileReader();
                        // Closure to capture the file information.
                        reader.onload = (function(theFile) {
                            return function(e) {
                                // Render thumbnail.
                                    $('#imageURL').attr('src',e.target.result);                         
                            };
                        })(f);
                        // Read in the image file as a data URL.
                        reader.readAsDataURL(f);
                    }

                });
            });
        }   
    });
    $( '#fileinput' ).imagePreview();
});

9

受@Josef答案的启发:

const fileToBase64 = async (file) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.readAsDataURL(file)
    reader.onload = () => resolve(reader.result)
    reader.onerror = (e) => reject(e)
  })

const file = event.srcElement.files[0];
const imageStr = await fileToBase64(file)

5

完整示例

HTML文件输入

<style>
.upload-button {
  background-color: grey;
}

.upload-button input{
  display:none;
}
</style>
<label for="upload-photo" class="upload-button">
    Upload file
    <input
     type="file"
     id="upload-photo"
    </input>
</label>

JS处理程序

document.getElementById("upload-photo").addEventListener("change", function({target}){
 if (target.files && target.files.length) {
      try {
        const uploadedImageBase64 = await convertFileToBase64(target.files[0]); 
        //do something with above data string 
      } catch() {
        //handle error
      }
    }
})

function convertFileToBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result);
    // Typescript users: use following line
    // reader.onload = () => resolve(reader.result as string);
    reader.onerror = reject;
  });
}

这是基于mb21的答案。 - Nitin Jadhav
1
as关键字在JavaScript中似乎不起作用,我认为你把答案放在TypeScript中了。 - Abhishek Mathur
@AbhishekMathur 谢谢,我已经更新了答案!干杯 - Nitin Jadhav

1

在自己的努力奋斗后,我已经实现了适用于支持它的浏览器(Chrome、Firefox和尚未发布的Safari 6)的FileReader,以及一个PHP脚本,将POST的文件数据作为Base64编码数据回显给其他浏览器。


0

那么为什么不同意系统用户从已知文件夹中选择图像?或者他们可以设置自己选择的图像文件夹。

大多数浏览器不支持完整路径,但您可以获取文件名,例如“image.png”

使用PHP内置函数进行编码:

@$picture_base64 = base64_encode( file_get_contents($image_file_name)  );

如果路径未找到,则符号@将抑制错误,但变量$picture_base64的结果将为null,所以我猜你也像我一样可以接受null,否则在继续之前请检查null。

在HTML中,您可以选择图像文件名作为输入,例如“image.png”(但不是完整路径)。

<input type="file" name="image" id="image" >

然后在 PHP 中你可以这样做:

$path = "C:\\users\\john\\Desktop\\images\\"
@$picture_base64 = base64_encode( file_get_contents( $path. $_POST['image']);

那么 $picture_base64 就会变成类似这样的东西

"AQAAAAMAAAAHAAAADwAAAB8AAAA/AAAAfwAAAP8AAAD/AQAA/w"

-1

我开始认为在 HTML5 彻底普及之前,对于我的情况使用“iframe”进行 Ajax 风格上传可能是一个更好的选择,这样我就不必支持应用程序中旧版浏览器了!


只是好奇,为什么不直接使用[http://www.webtoolkit.info/javascript-base64.html#],然后将`input = Base64._utf8_encode(input);output = Base64._utf8_decode(output);`注释掉呢?除了UTF-8转换错误之外还有其他问题吗? - shr
@shr 我不知道如何在每个浏览器中抓取二进制文件数据以输入这些方法? $('#inputid').files[0] (就我所知,在IE7中无法使用)。如果那很容易呢?var output = Base64.encode($('#inputid').files[0]) ?? - jordan.baucke

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