Преобразуйте URI данных в файл, затем добавьте в FormData

225

Я пытаюсь переустановить загрузчик изображений HTML5, например, на сайте Mozilla Hacks, но это работает с браузерами WebKit, Часть задачи состоит в извлечении файла изображения из объекта canvas и добавлении его в объект FormData для загрузки.

Проблема заключается в том, что while canvas имеет функцию toDataURL для возврата представления файла изображения, объект FormData принимает объекты File или Blob только из API файлов.

Решение Mozilla использовало следующую функцию только для Firefox на canvas:

var file = canvas.mozGetAsFile("foo.png");

..., который недоступен в браузерах WebKit. Лучшее решение, которое я мог бы подумать, - найти способ конвертировать URI данных в объект File, который, как я думал, может быть частью File API, но я не могу на всю жизнь найти что-то для этого.

Возможно ли это? Если нет, то какие-либо альтернативы?

Спасибо.

  • 0
    Если вы хотите сохранить DataURI изображения на сервере: stackoverflow.com/a/50131281/5466401
Теги:
webkit

13 ответов

353
Лучший ответ

Поиграв с несколькими вещами, мне удалось понять это сам.

Прежде всего, это преобразует dataURI в Blob:

function dataURItoBlob(dataURI) {
    // convert base64/URLEncoded data component to raw binary data held in a string
    var byteString;
    if (dataURI.split(',')[0].indexOf('base64') >= 0)
        byteString = atob(dataURI.split(',')[1]);
    else
        byteString = unescape(dataURI.split(',')[1]);

    // separate out the mime component
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to a typed array
    var ia = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    return new Blob([ia], {type:mimeString});
}

Оттуда добавление данных в форму, так что она будет загружена как файл:

var dataURL = canvas.toDataURL('image/jpeg', 0.5);
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
  • 24
    Почему это всегда происходит ... Вы пытаетесь решить проблему часами часами с помощью поисков SO здесь и там. Тогда вы отправляете вопрос. В течение часа вы получите ответ на другой вопрос. Не то чтобы я жаловался ... stackoverflow.com/questions/9388412/…
  • 0
    @stoive Я могу создать Blob, но не могли бы вы объяснить, как вы строите POST или PUT для S3?
Показать ещё 17 комментариев
129

BlobBuilder и ArrayBuffer теперь устарели, вот код верхнего комментария, обновленный конструктором Blob:

function dataURItoBlob(dataURI) {
    var binary = atob(dataURI.split(',')[1]);
    var array = [];
    for(var i = 0; i < binary.length; i++) {
        array.push(binary.charCodeAt(i));
    }
    return new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
}
  • 2
    Просто идея: array=[]; array.length=binary.length; ... array[i]=bina ... и т. д. Таким образом, массив предварительно выделен. Это спасает push () от необходимости расширять массив каждую итерацию, и мы обрабатываем, возможно, миллионы элементов (= байтов) здесь, так что это важно.
  • 5
    Это не работает в Safari.
Показать ещё 4 комментария
48

Это работает в iOS и Safari.

Вам нужно использовать решение Stoive ArrayBuffer, но вы не можете использовать BlobBuilder, как указывает vava720, поэтому здесь mashup для обоих.

function dataURItoBlob(dataURI) {
    var byteString = atob(dataURI.split(',')[1]);
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }
    return new Blob([ab], { type: 'image/jpeg' });
}
  • 11
    Большой! Но вы все еще можете сохранить динамическую строку MIME, как в решении Стоива, я полагаю? // выделяем компонент mime var mimeString = dataURI.split (',') [0] .split (':') [1] .split (';') [0]
  • 0
    Что с резервной версией для iOS6 с префиксом webkit? Как вы справляетесь с этим?
21

В Firefox есть методы canvas.toBlob() и canvas.mozGetAsFile().

Но другие браузеры этого не делают.

Мы можем получить dataurl с холста, а затем преобразовать dataurl в объект blob.

Вот моя функция dataURLtoBlob(). Это очень коротко.

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

Используйте эту функцию с FormData для обработки вашего холста или dataurl.

например:

var dataurl = canvas.toDataURL('image/jpeg',0.8);
var blob = dataURLtoBlob(dataurl);
var fd = new FormData();
fd.append("myFile", blob, "thumb.jpg");

Кроме того, вы можете создать метод HTMLCanvasElement.prototype.toBlob для браузера браузера без gecko.

if(!HTMLCanvasElement.prototype.toBlob){
    HTMLCanvasElement.prototype.toBlob = function(callback, type, encoderOptions){
        var dataurl = this.toDataURL(type, encoderOptions);
        var bstr = atob(dataurl.split(',')[1]), n = bstr.length, u8arr = new Uint8Array(n);
        while(n--){
            u8arr[n] = bstr.charCodeAt(n);
        }
        var blob = new Blob([u8arr], {type: type});
        callback.call(this, blob);
    };
}

Теперь canvas.toBlob() работает для всех современных браузеров не только firefox. например:

canvas.toBlob(
    function(blob){
        var fd = new FormData();
        fd.append("myFile", blob, "thumb.jpg");
        //continue do something...
    },
    'image/jpeg',
    0.8
);
  • 1
    Упомянутый здесь polyfill для canvas.toBlob является правильным способом решения этой проблемы.
  • 1
    Я хотел бы подчеркнуть последнее в этом посте: «Теперь canvas.toBlob () работает для всех современных браузеров».
Показать ещё 2 комментария
19

Благодаря @Stoive и @vava720 я объединил их таким образом, избегая использования устаревших BlobBuilder и ArrayBuffer

function dataURItoBlob(dataURI) {
    'use strict'
    var byteString, 
        mimestring 

    if(dataURI.split(',')[0].indexOf('base64') !== -1 ) {
        byteString = atob(dataURI.split(',')[1])
    } else {
        byteString = decodeURI(dataURI.split(',')[1])
    }

    mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0]

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

    return new Blob([new Uint8Array(content)], {type: mimestring});
}
12

Развивающийся стандарт выглядит как canvas.toBlob(), а не canvas.getAsFile(), поскольку Mozilla рискует угадать.

Я не вижу ни одного браузера, поддерживающего его: (

Спасибо за эту замечательную тему!

Кроме того, любой, кто пытается принять ответ, должен быть осторожен с BlobBuilder, поскольку я нахожу поддержку ограниченной (и имен):

    var bb;
    try {
        bb = new BlobBuilder();
    } catch(e) {
        try {
            bb = new WebKitBlobBuilder();
        } catch(e) {
            bb = new MozBlobBuilder();
        }
    }

Вы использовали другую библиотеку polyfill для BlobBuilder?

  • 0
    Я использовал Chrome без полифилов и не помню, чтобы сталкивался с пространством имен. Я с нетерпением canvas.toBlob() - он кажется гораздо более подходящим, чем getAsFile .
  • 1
    BlobBuilder кажется устаревшим в пользу Blob
Показать ещё 2 комментария
8

Мой предпочтительный способ canvas.toBlob()

Но так или иначе, это еще один способ конвертировать base64 в blob с помощью fetch ^^,

var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="

fetch(url)
.then(res => res.blob())
.then(blob => {
  var fd = new FormData()
  fd.append('image', blob, 'filename')
  
  console.log(blob)

  // Upload
  // fetch('upload', {method: 'POST', body: fd})
})
  • 0
    что такое fetch и как это актуально?
  • 0
    Fetch - это современный метод ajax, который вы можете использовать вместо XMLHttpRequest поскольку URL-адрес данных является просто URL-адресом. Вы можете использовать ajax для извлечения этого ресурса, и у вас есть возможность решить, хотите ли вы использовать его как blob, arraybuffer или text
Показать ещё 1 комментарий
5
var BlobBuilder = (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder);

можно использовать без улова try.

Благодарим check_ca. Отличная работа.

  • 1
    Это все равно выдаст ошибку, если браузер поддерживает устаревший BlobBuilder. Браузер будет использовать старый метод, если он его поддерживает, даже если он поддерживает новый метод. Это не желательно, см. Подход Криса Боско ниже
4

Исходный ответ Stoive легко фиксируется путем изменения последней строки для размещения Blob:

function dataURItoBlob (dataURI) {
    // convert base64 to raw binary data held in a string
    // doesn't handle URLEncoded DataURIs
    var byteString;
    if (dataURI.split(',')[0].indexOf('base64') >= 0)
        byteString = atob(dataURI.split(',')[1]);
    else
        byteString = unescape(dataURI.split(',')[1]);
    // separate out the mime component
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to an ArrayBuffer
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    // write the ArrayBuffer to a blob, and you're done
    return new Blob([ab],{type: mimeString});
}
3

Вот версия ES6 Отвечайте ответ:

export class ImageDataConverter {
  constructor(dataURI) {
    this.dataURI = dataURI;
  }

  getByteString() {
    let byteString;
    if (this.dataURI.split(',')[0].indexOf('base64') >= 0) {
      byteString = atob(this.dataURI.split(',')[1]);
    } else {
      byteString = decodeURI(this.dataURI.split(',')[1]);
    }
    return byteString;
  }

  getMimeString() {
    return this.dataURI.split(',')[0].split(':')[1].split(';')[0];
  }

  convertToTypedArray() {
    let byteString = this.getByteString();
    let ia = new Uint8Array(byteString.length);
    for (let i = 0; i < byteString.length; i++) {
      ia[i] = byteString.charCodeAt(i);
    }
    return ia;
  }

  dataURItoBlob() {
    let mimeString = this.getMimeString();
    let intArray = this.convertToTypedArray();
    return new Blob([intArray], {type: mimeString});
  }
}

Использование:

const dataURL = canvas.toDataURL('image/jpeg', 0.5);
const blob = new ImageDataConverter(dataURL).dataURItoBlob();
let fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
1

сделать это простым: D

function dataURItoBlob(dataURI,mime) {
    // convert base64 to raw binary data held in a string
    // doesn't handle URLEncoded DataURIs

    var byteString = window.atob(dataURI);

    // separate out the mime component


    // write the bytes of the string to an ArrayBuffer
    //var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    // write the ArrayBuffer to a blob, and you're done
    var blob = new Blob([ia], { type: mime });

    return blob;
}
0

toDataURL дает вам строку, и вы можете поместить эту строку в скрытый ввод.

  • 0
    Не могли бы вы привести пример? Я не хочу загружать строку base64 (что делает <input type=hidden value="data:..." /> ), я хочу загрузить данные файла (например, что <input type="file" /> делает, за исключением того, что вы не можете устанавливать value свойства для них).
  • 0
    Это должен быть комментарий, а не ответ. Пожалуйста, дайте ответ с подробным объяснением. @Cat Chen
-2

У меня была такая же проблема, как у Равиндера Пайлала, и я нашел ответ. Попробуйте следующее:

var dataURL = canvas.toDataURL("image/jpeg");

var name = "image.jpg";
var parseFile = new Parse.File(name, {base64: dataURL.substring(23)});
  • 2
    Вы действительно предлагаете использовать Parse.com? Вы должны отметить, что ваш ответ требует зависимости!
  • 2
    WTF? Почему кто-либо будет загружать код base64 изображения на сервер PARSE, а затем загружать? Когда мы можем напрямую загрузить base64 на наши серверы, главное, чтобы для загрузки строки base64 или файла изображения потребовались одни и те же данные. И если вы просто хотите разрешить пользователю загружать изображение, вы можете использовать это window.open(canvas.toDataURL("image/jpeg"))
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню