将Base64编码的图像保存到Firebase存储

13

使用Firebase 3.0.x版本,能否将Base64编码的图像保存到新的Firebase Storage服务中?

我在浏览器中使用canvas对上传前的图片进行调整大小,并将其输出为Base64 JPEG格式。我知道Storage API可以接受Blobs,但我的当前项目需要支持IE9。


一个对我起作用的解决方案在https://dev59.com/wa7la4cB1Zd3GeqPXyFx#52174058提供。 - Yossi
7个回答

22

您只需使用putString函数,而无需将BASE64转换为blob。

firebase.storage().ref('/your/path/here').child('file_name')
.putString(your_base64_image, ‘base64’, {contentType:’image/jpg’});

确保在使用函数putString时,将元数据{contentType: 'image/jpg'} 作为第三个参数传递(可选),以便您以图像格式检索数据。

或者简单来说:

uploadTask = firebase.storage().ref('/your/path/here').child('file_name').putString(image, 'base64', {contentType:'image/jpg'});
uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED, // or 'state_changed'
  function(snapshot) {
    // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
    var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
    console.log('Upload is ' + progress + '% done');
    switch (snapshot.state) {
      case firebase.storage.TaskState.PAUSED: // or 'paused'
        console.log('Upload is paused');
        break;
      case firebase.storage.TaskState.RUNNING: // or 'running'
        console.log('Upload is running');
        break;
    }
  }, function(error) {
    console.log(error);
}, function() {
  // Upload completed successfully, now we can get the download URL
  var downloadURL = uploadTask.snapshot.downloadURL;
});
你可以使用downloadURL来保存到firebase.database(),或将其作为<img>标记的src。

你从哪里获取了第一个对象“firebase”?这个对象是什么? - oded bartov
@odedbartov 那是 Firebase 实例。 - Niño Angelo Orlanes Lapura

10

最新版本的Firebase SDK支持base64图片上传。只需使用Firebase Storage中的putString方法即可。

https://firebase.google.com/docs/reference/js/firebase.storage

需要注意的是,有时候你会遇到带有不必要空格的base64字符串。例如,我发现cordova相机插件返回的base64带有不必要的空格。由于JavaScript无法执行其本地atob函数(Firebase JS在其底层进行了处理),因此Storage SDK将无法上传此类字符串。您需要去除空格 - 参见DOM Exception 5 INVALID CHARACTER error on valid base64 image string in javascript


我应该在哪里使用putString方法?Firebase-Admin不支持它。 - oded bartov

6

是的,现在是可能的。你应该使用Firebase Storage的新方法putString。你可以在这里阅读规范here

因此,Firebase规范表示,现在有两种方法可以存储Base64字符串和Base64url字符串:

// Base64 formatted string
var message = '5b6p5Y+344GX44G+44GX44Gf77yB44GK44KB44Gn44Go44GG77yB';
ref.putString(message, 'base64').then(function(snapshot) {
  console.log('Uploaded a base64 string!');
});

// Base64url formatted string
var message = '5b6p5Y-344GX44G-44GX44Gf77yB44GK44KB44Gn44Go44GG77yB';
ref.putString(message, 'base64url').then(function(snapshot) {
  console.log('Uploaded a base64url string!');
})

根据我的经验,在使用putString(message, 'base64url')时,会不断返回关于Base64字符串代码格式不正确的错误,错误信息为"storage/invalid-format", message: "Firebase Storage: String does not match format 'base64': Invalid character found"。解决方法是去掉字符串开头的data:image/jpeg;base64,,并改用第一种方法putString(message, 'base64')。这样就可以正常工作了。


1
我通过添加以下代码解决了这个问题:let file = image.split(',')[1] storeRef(file,'base64', {contentType:'image/png'}); //仅获取图像数据 - Miguel Peguero
你是从哪里获取第一个对象 'ref' 的?这个对象是什么? - oded bartov

2
如果您使用canvas.toBlob(),您将获得需要传递到Firebase Storage的byte[]
快速示例:
function save() {
    var canvas = document.getElementById("canvas");
    canvas.toBlob(blob => {
      var storage = firebase.app().storage().ref();
      var name = id + "/" + (new Date()).getTime() + ".png";
      var f = storage.child("drawings/" + name);
      var task = f.put(blob);
      task.on('state_changed', function(snapshot) {
      }, function(error) {
        console.error("Unable to save image.");
        console.error(error);
      }, function() {
        var url = task.snapshot.downloadURL;
        console.log("Saved to " + url);

        var db = firebase.database();
        var chats = db.ref().child("chats");
        chats.child(id).child("drawingURL").set(url);
      });
    });
  };

否则您需要自己转换base64,例如使用atob()

我希望我能使用.toBlob,但Safari和IE<10根本不支持它。即使是polyfill也不受IE9的支持。 - Sebastian Sandqvist
你也可以手动将b64转换为blob,参见我的答案。 - Hobbyist

1

使用Firebase SDK 9进行Web开发(截至2022年5月)

import { getStorage, ref, uploadString } from "firebase/storage";

const storage = getStorage();
const storageRef = ref(storage, 'some-child');

// Data URL string
const message4 = 'data:text/plain;base64,5b6p5Y+344GX44G+44GX44Gf77yB44GK44KB44Gn44Go44GG77yB';
uploadString(storageRef, message4, 'data_url').then((snapshot) => {
  console.log('Uploaded a data_url string!');
});

1
这里有两个数值,我用它们来帮助支持 .toBlob(),虽然它的性能较低,但它可以完成工作。
该方法接受base64字符串、内容类型(例如:image/png)和一个回调函数,用于在使用atob()构建blob时触发。
var b64_to_blob = function(b64_data, content_type, callback) {
        content_type = content_type || '';
        var slice_size = 512;

        var byte_characters = atob(b64_data);
        var byte_arrays = [];

        for(var offset = 0; offset < byte_characters.length; offset += slice_size) {
            var slice = byte_characters.slice(offset, offset + slice_size);

            var byte_numbers = new Array(slice.length);
            for(var i = 0; i < slice.length; i++) {
                byte_numbers[i] = slice.charCodeAt(i);
            }

            var byte_array = new Uint8Array(byte_numbers);

            byte_arrays.push(byte_array);
        }

        var blob = new Blob(byte_arrays, {type: content_type});
        callback(blob);
    };

当需要时,我使用这种方法获取直接链接的base64值,我的情况是在用户在我的应用程序上注册时下载他们的Facebook照片。

    var image_link_to_b64 = function(url, content_type, callback) {
        var image = new Image();
        image.crossOrigin = 'Anonymous';
        image.onload = function() {
            var canvas = document.createElement('CANVAS');
            var context = canvas.getContext('2d');
            var data_url;
            canvas.height = this.height;
            canvas.width = this.width;
            context.drawImage(this, 0, 0);
            data_url = canvas.toDataURL(content_type);
            data_url = data_url.substr(22);
            callback(data_url);
            canvas = null;
        };
        image.src = url;
    };

以下是将信息添加到Firebase存储时的外观。
$fileService.image_link_to_b64(facebook_info.photoURL + '?width=512&height=512', 'image/png', function(b64) {
     $fileService.b64_to_blob(b64, 'image/png', function(blob) {
        $fileService.upload_user_photo(blob, 'image/png', function(url) {
            // Firebase storage download url here
        }
     }
  }

如果您想知道,upload_user_photo 只是将照片上传到 Firebase 存储:

var upload_user_photo = function(data, blob, callback) {
        var upload_task = user_photo_reference.child('user_photo').put(data);
        upload_task.on('state_changed', function(snapshot) {

        }, function(error) {
            alert('error: ', error);
        }, function() {
            callback(upload_task.snapshot.downloadURL);
        });
    };]

针对IE9,请参考此polyfill:https://github.com/eligrey/Blob.js/blob/master/Blob.js


1

使用Google Cloud Storage API,这个解决方案适用于我。
但是,通过使用 ref put 方法替换 file.save,它也应该适用于 Firebase。

const file = storage.file(file_path_in_gs)
const contents = new Uint8Array(Buffer.from(base64ImgStr, 'base64'))
  file.save(contents,
    {
      contentType: img_type,
      metadata: {
        metadata: {
          contentType: img_type,
          firebaseStorageDownloadTokens: uuid()
        }
      }
    }
    , () => { })

但是你如何获取这个'ref'呢?我已经为此苦苦挣扎了很长时间。 - oded bartov
const contents = new Uint8Array(Buffer.from(base64ImgStr, 'base64')) 这一行非常重要。我之前尝试将 contents=base64ImgStr,但是没有成功。 - Wilfred Almeida

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