Подтвердить что ты не робот

Создание Firefox и Chrome для загрузки изображения под определенным именем

Учитывая https://www.example.com/image-list:

...
<a href="/image/1337">
  <img src="//static.example.com/thumbnails/86fb269d190d2c85f6e0468ceca42a20.png"/>
</a>
<a href="//static.example.com/full/86fb269d190d2c85f6e0468ceca42a20.png"
   download="1337 - Hello world!.png">
  Download
</a>
...

Это среда пользователя script, поэтому у меня нет доступа к конфигурации сервера. Таким образом:

  • Я не могу заставить сервер принимать дружественные имена файлов, например https://static.example.com/full/86fb269d190d2c85f6e0468ceca42a20 - 1337 - Hello World!.png.
  • Я не могу настроить совместное использование ресурсов Cross-Origin. www.example.com и static.example.com отделены стенкой CORS по дизайну.

Как заставить Firefox и Chrome отображать диалог "Сохранить файл как" с предлагаемым именем файла "1337 - Hello world!.png", когда пользователь нажимает ссылку "Загрузить"?

После некоторых неудачных попыток и поиска в Google я понял эти проблемы:

  • Firefox полностью игнорирует существование атрибута download для некоторых типов изображений MIME.
  • Firefox полностью игнорирует существование атрибута download для межсайтовых ссылок.
  • Chrome полностью игнорирует значение атрибута download для межсайтовых ссылок.

Все эти моменты не имеют для меня никакого смысла, все выглядят как "пусть случайные нечувствительные ограничения на эту функцию", но я должен принять их в качестве своей среды.

Существуют ли какие-либо способы решения проблемы?


Справочная информация. Я пишу пользователя script для платы изображений, которая использует хеши MD5 в качестве имен файлов. Я хочу сделать экономию с удобными именами проще. Все, что приблизит меня к этому, было бы полезно.

Я думаю, что я могу обойти ограничения, используя URL-адреса объектов для blobs и локальный прокси-сервер с взломанными заголовками CORS, но эта настройка явно не поддается разумному. Сохранение через холст может работать (в этом случае также "защищены" изображениями CORS?), Но это либо приведет к сжатию с двойной потерей, либо к преобразованию с потерями в без потерь, если файлы JPEG не будут хорошими.

4b9b3361

Ответ 1

Все современные браузеры будут игнорировать атрибут загрузки в теге привязки для URL-адреса перекрестного происхождения.

Ссылка: https://html.spec.whatwg.org/multipage/links.html#downloading-resources

По словам производителей спекуляций, это представляет собой лазейку безопасности, так как пользователь может обмануть загрузку вредоносных файлов при просмотре защищенного сайта, полагая, что файл также происходит из того же защищенного сайта.

Любой интересный разговор для реализации этой функции в браузере firefox можно найти здесь: https://bugzilla.mozilla.org/show_bug.cgi?id=676619


[Редактировать по Athari]

Цитата из спецификации:

Это может быть опасно, поскольку, например, враждебный сервер может пытаться заставить пользователя неосознанно загружать личную информацию, а затем повторно загружать его на враждебный сервер, обманывая пользователя, думая, что данные взяты из враждебный сервер.

Таким образом, пользователь заинтересован в том, чтобы пользователь каким-то образом уведомлялся о том, что данный ресурс исходит из совершенно другого источника, и для предотвращения путаницы любое предполагаемое имя файла из потенциально враждебного интерфейса должно игнорироваться.

Разъяснение таинственного сценария:

более серьезная проблема с загрузкой CORS - это если вредоносный сайт заставляет загружать файл в виде законного сайта, а некоторые получают доступ к его контенту. поэтому скажем, я загружаю страницу входящих сообщений пользователя gmail и исследую ее сообщения.

в этом случае злому сайту придется обмануть пользователя в загрузке файла и его загрузке на сервер, так что скажем, что у нас есть gmail.com/inbox.html, на самом деле содержит все почтовые сообщения пользователя и злоумышленника сайты предлагают ссылку для загрузки файла купона, который должен быть загружен на другой сайт зла. купон будет предлагать 30% скидку на новый Ipad. ссылка на скачивание фактически укажет на gmail.com/inbox.html и будет загружать ее как "30off.coupon", если пользователь загрузит этот файл и загрузит его, не проверив его содержимое, злодейский сайт получит пользовательский "купон", и, следовательно, его содержимое для входящих сообщений.

Важные замечания:

  • Google изначально не ограничивал атрибут загрузки CORS и был явно против этого. Позднее было принудительно настроить Chrome.

    Google выступал против использования CORS для этого.

  • Были предложены альтернативные решения с предоставлением пользователю предупреждения о загрузках с использованием перекрестного источника. Они были проигнорированы.

    Хорошо, что при загрузке из другого источника может появиться уведомление или запретить/разрешить (например, в случае API геолокации). Или не отправлять файлы cookie в случае запроса перекрестного происхождения с атрибутом загрузки.

  • Некоторые разработчики разделяют мнение о том, что ограничение слишком велико, сильно ограничивает использование этой функции и что сценарий настолько сложный, что пользователь, который сделает это, легко загрузит и запустит исполняемый файл. Их мнение было проигнорировано.

    Случай с разрешенными загрузками с кросс-началом сосредоточен вокруг предпосылки, что посетители [злого] сайта (например, discountipads.com) могут неосознанно загружать файл с сайта, содержащего их личную информацию (например, gmail.com ) и сохраните его на диск с использованием вводящего в заблуждение имени (например, "discount.coupon" ) И затем перейдите к другой вредоносной странице, где они вручную загружают тот же самый файл, который они только что загрузили. На мой взгляд, это довольно надуманно, и любой, кто будет поддаваться такому тривиальному обману, возможно, не принадлежит в первую очередь онлайн. Я имею в виду c'mon... Нажмите здесь, чтобы загрузить наше специальное предложение скидок, а затем повторно загрузить его через нашу специальную форму! Шутки в сторону? Загрузите наше специальное предложение, а затем отправьте его по электронной почте на этот адрес Yahoo для получения большой скидки! Неужели люди, которые падают за эти вещи, даже знают, как делать вложения электронной почты?

    Я все для защиты браузера, но если у хороших людей Chromium нет проблем с этим, я не понимаю, почему Firefox должен полностью изгнать его. По крайней мере, я хотел бы видеть предпочтение примерно: config, чтобы включить cross-origin @download для "продвинутых" пользователей (по умолчанию это значение false). Еще лучше будет поле для подтверждения, похожее на: "Хотя эта страница зашифрована, информация, которую вы отправляете через эту форму, не будет" или: "Эта страница запрашивает установку аддонов" или: "Файлы, загруженные из Интернета, могут нанести вред ваш компьютер" или даже: "Неверный сертификат безопасности этой страницы"... знаешь, что я имею в виду? Существует множество способов повысить информированность пользователей и сообщить им, что это может быть небезопасно. Один дополнительный клик и короткая (или длинная?) Задержка достаточно, чтобы позволить им оценить риск.

    По мере роста сети, а использование CDN растет, а присутствие продвинутых веб-приложений растет, а потребность в управлении файлами, размещаемых на серверах, растет, такие функции, как @download, станут более важными. И когда браузер, такой как Chrome, полностью поддерживает его, в то время как Firefox этого не делает, это не победа для Firefox.

    Короче говоря, я считаю, что смягчение потенциального злонамеренного использования @download простым игнорированием атрибута в сценариях с пересекающимся происхождением является ужасно непродуманным движением. Я не говорю, что риск совершенно несуществующий, совсем наоборот: я говорю, что есть много рискованных вещей, которые он делает онлайн в течение его дня... загрузка ЛЮБОГО файла высока среди них. Почему бы не обойти эту проблему с хорошо продуманным пользовательским интерфейсом?

В целом, учитывая широкое использование CDN и намеренное размещение контента, созданного пользователями в другом домене, основное использование атрибута загрузки указывает имя файла для загрузки blob (URL.createObjectURL) и тому подобное. Он не может использоваться во многих конфигурациях и, конечно же, не очень полезен в пользовательских сценариях.

Ответ 2

Попробуйте что-то вроде:

  • Сначала получите внешнее изображение на ваш сервер.
  • Вернуть выбранное изображение с вашего сервера.
  • Динамически создайте привязку с именем download и .click() it!

в то время как выше было просто довольно короткий список подсказок... попробуйте:

на www.example.com поместите a fetch-image.php с этим контентом:

<?php
$url = $_GET["url"];                     // the image URL
$info = getimagesize($url);              // get image data
header("Content-type: ". $info['mime']); // act as image with right MIME type
readfile($url);                          // read binary image data
die();

или с любым другим серверным языком, который достигает того же самого.

Вышеупомянутое должно возвращать любое внешнее изображение, сидящее на вашем домене.

На странице image-list вы можете попробовать:

<a 
  href="//static.example.com/thumbnails/86fb269d190d2c85f6e0468ceca42a20.png" 
  download="1337 - Hello world!.png">DOWNLOAD</a>

и это JS:

function fetchImageAndDownload (e) {
    e.preventDefault(); // Prevent browser default download stuff...

    const url = this.getAttribute("href");       // Anchor href 
    const downloadName = this.download;          // Anchor download name

    const img = document.createElement("img");   // Create in-memory image
    img.addEventListener("load", () => {
        const a = document.createElement("a");   // Create in-memory anchor
        a.href = img.src;                        // href toward your server-image
        a.download = downloadName;               // :)
        a.click();                               // Trigger click (download)
    });
    img.src = 'fetch-image.php?url='+ url;       // Request image from your server

}

[...document.querySelectorAll("[download]")].forEach( el => 
    el.addEventListener("click", fetchImageAndDownload)
);

Вы должны увидеть, наконец, изображение, загруженное как

1337 - Привет, мир!.png

вместо 86fb269d190d2c85f6e0468ceca42a20.png, как это было в случае.

Уведомление. Я не уверен в возможности одновременных запросов к fetch-image.php - убедитесь, что вы протестировали.

Ответ 3

Вы можете попробовать сделать это

var downloadHandler = function(){
    var url = this.dataset.url;
    var name = this.dataset.name;
    // by this you can automaticaly convert any supportable image type to other, it is destination image format
    var mime = this.dataset.type || 'image/jpg';
    var image = new Image();
    //We need image and canvas for converting url to blob.
    //Image is better then recieve blob through XHR request, because of crossOrigin mode
image.crossOrigin = "Anonymous";
   
    
    image.onload = function(oEvent) {
      //draw image on canvas
      var canvas = document.createElement('canvas');
      canvas.width = this.naturalWidth;
      canvas.height = this.naturalHeight;
      canvas.getContext('2d').drawImage(this, 0, 0, canvas.width, canvas.height);
      // get image from canvas as blob
      var binStr = atob( canvas.toDataURL(mime).split(',')[1] ),
            len = binStr.length,
            arr = new Uint8Array(len);

        for (var i = 0; i < len; i++ ) {
          arr[i] = binStr.charCodeAt(i);
        }

      var blob = new Blob( [arr], {type: mime} );
      //IE not works with a.click() for downloading
      if (window.navigator && window.navigator.msSaveOrOpenBlob) 			 {
   	  window.navigator.msSaveOrOpenBlob(blob, name);
      } else {
          var a = document.createElement("a");  
          a.href = URL.createObjectURL(blob);                     
          a.download = name;              
          a.click();  
      }
    };

    image.src = url;
}

document.querySelector("[download]").addEventListener("click", downloadHandler)
<button 
data-name="file.png" 
data-url="https://tpc.googlesyndication.com/simgad/14257743829768205599"
data-type="image/png"
download>
download
</button>

Ответ 4

Если у вас есть доступ к обоим, backend и внешнему коду, вот шаги, которые могут помочь вам

Я не уверен, какой тип бэкэнд-языка вы используете, поэтому я просто объясню, что нужно делать без кода.

В бэкэнд для предварительного просмотра ваш код должен работать так, как есть, если вы получите в строке запроса что-то вроде ?download=true, тогда ваш бэкэнд должен упаковать файл как размещенный контент, другими словами, вы бы использовали заголовок ответа content-disposition. Это откроет вам возможность добавлять дополнительные атрибуты в контент, например filename, поэтому он может быть похожим на это

Content-Disposition: attachment; filename="filename.jpg"

Теперь, во фронте, любая ссылка, которая должна вести себя как кнопка загрузки, должна содержать ?download=true в параметре запроса href И target="_blank", который временно откроет другую вкладку в браузере для цели загрузки.

<a href="/image/1337">
  <img src="//static.example.com/thumbnails/86fb269d190d2c85f6e0468ceca42a20.png"/>
</a>
<a href="//static.example.com/full/86fb269d190d2c85f6e0468ceca42a20.png?download=true" target="_blank" title="1337 - Hello world!.png">
  Download Full size
</a>

Я знаю, что это работает без настройки CORS, и если пользователь нажимает ссылку на скачивание, но я никогда не тестировал диалоговое окно "Сохранить как" в браузере... и для его создания потребуется некоторое время, поэтому, пожалуйста, попробуйте.

Ответ 5

Соответствующий хром-API...

https://developer.chrome.com/extensions/downloads#event-onDeterminingFilename

Пример...

chrome.downloads.onDeterminingFilename.addListener(function(item,suggest){

 suggest({filename:"downloads/"+item.filename}); // suggest only the folder

 suggest({filename:"downloads/image23.png"}); // suggest folder and filename

});

Упс... ты на стороне сервера, но я принял сторону клиента! Я оставлю это здесь, хотя в случае, если кому-то это понадобится.

Ответ 6

Я могу переименовать изображения base64 с сохранением в качестве поля ввода.

Я думаю, ваш лучший выбор - создать свой собственный "сохранить как". Когда пользователь нажимает "загрузить", отобразите "Имя файла: {поле ввода]" и кнопку "Сохранить". Нажмите кнопку сохранения, чтобы изменить имя файла, а затем вызвать функцию загрузки.

function save2() {
    var gh = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADNQTFRFAAAAPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09EvTTBAAAABB0Uk5TAA8fLz9PX29/j5+vv8/f7/rfIY4AAARsSURBVHja7VvZkusqDAyrAQvQ/3/teZjExrHDZoGrbl0/z0RtLY1oya/X/8+Nh/NHzYuAGPRz9llERET1GACJiIgYngaAkmvNnwTgERHtgwDefngawDofgDkAwPlp4I4AzHQA/gggstk0jF/P7ELQ3wBQPBoBRPRzTyI8P/bBGvh79FMstFXCvDSAt0kHzyBYNv7jj/iAx48DEiwzEfj0AFi/EEw4F+2B/5mfXQsbB4ZDQOKGwM2ioE+4hUdEm3jCjzybRbw4gIXkrxfbySnqCfYhS48rG23fs/wRGYdcGIQv1PsOcIgTkp//xTcs4WbyTEjs67pmFfh8+3+X1s0Jy3z7rxezaZ9EdTjI2MC1MpA37LqN65kjdoJuPmtUUpC40NmvLy2WntM3OcH09RupE8KdMLjefufgBE1gvz2blnj/2pDY7wikSPold9M+dCVSWpDuln1HUMLuCfsHEndP2H+9uO+kJEfVaicNq+zin9udxY6gQcrRlFeNHUG1oCfpjpIjAtmaukQXHRabpJwdMNlFSzZFdL3Dv4WkrlH4lyH6Y6jOgj0BSPUGWV0InrQAztISr2UgahFe1r3XJgHRC9C+qhK3CqC/4H6Sm1XV64ApCKt5NegOgFTGGGPMIlnhx22NA64zhUsppTxVMtcuvY5hcCqX31DhgAu+EgZ+WLjSjoPJvF6mBH5lIFvC7wHBJ7kAAAByjFdkAvdDg0o7/PPByiOCSSIvbfhBo6HExvES/ftwjOs7v7iyoZCl0qhMhHWpDQoX9QvH/xJd+osriAbr9ZktEQONCm3yAD5EEU833YWIlgsA1PD5UwGAGz4DLAAIw0eAeQBs/CTaZi2o8VNYyAIwP2qAHsCSZYGR6xD5xtgPTwGeBzB+I0Xlj+Oajo2kCEK+GRqfg2sWwEAaKhCNLDdsRCkgnwLg8kEeDyDmLQwHoAp3w+EA1kJPPBoAL6lEYnAZmuLtfCwRbToZLwEYNP7X5Vs33NEFuI15BS6U7+auuydmGkoKXI1Kt9RlIZPHIIllLbfzWwboCm2AF480b7WUQkipDWySkhPlg7ggU9apWPFqkWzV2TZC1Am1a1UMltMWW8F6Xve4qpRCX86U3ZQkcEtFF79UKtW8RSJnsvr+IDK7N23HRScH+mrtWQ/RCF3D+DYOaM337bOKftvQ78iKps3fjbDIrkeX22cVLqAKAovVFfD1DzRi/V4AgbWmDMW8ivmO7Qto9FlV/FvGr5xsZilj3/hXI00UTPcKi6PYgkrXR5qnb/72ZuRho03fSF5E1xOGg7qvb5VPz2akTmcbnT48LExDCysycxitdGfRcWUbar2gvj59cDfqyH3NoMpNyt+k5r77t1B+tb/eZNzJtTt1y+4umXM49b9g1AmFUPvloDdzqsppDweA/RuSOoDLv6D7GvRAKPUP5ceo3DWbX4nFXm5iy8ubEfqCWiut22HDDqZcyBuP6zL6s0euLVzbBqunfWbFpTZmhfdjjVFy9seO/6nnH0Mpp/3TjvofAAAAAElFTkSuQmCC"

    var a  = document.createElement('a');
    a.href = gh;
    a.download = document.getElementById("input").value;
   

    a.click()
    
}
Filename: <input id="input" name="input" placeholder="enter image name"></insput><button onclick="save2()">Save</button>