Kullanıcının indirmesi için sunucuda değil, bellekte bir dosya nasıl oluşturulur?


824

İstemci tarafında bir metin dosyası oluşturmanın ve kullanıcıdan sunucuyla herhangi bir etkileşimde bulunmadan dosyayı indirmesini istememin bir yolu var mı? Makinelerine doğrudan yazamadığımı biliyorum (güvenlik ve hepsi), ancak onları kaydedip kaydetmelerini isteyebilir miyim?


Yanıtlar:


432

Veri URI'lerini kullanabilirsiniz. Tarayıcı desteği değişiklik gösterir; Wikipedia'ya bakınız . Misal:

<a href="data:application/octet-stream;charset=utf-16le;base64,//5mAG8AbwAgAGIAYQByAAoA">text file</a>

Octet-stream bir indirme istemini zorlamak içindir. Aksi takdirde, muhtemelen tarayıcıda açılır.

CSV için şunları kullanabilirsiniz:

<a href="data:application/octet-stream,field1%2Cfield2%0Afoo%2Cbar%0Agoo%2Cgai%0A">CSV Octet</a>

JsFiddle demosunu deneyin .


19
Bu bir çapraz tarayıcı çözümü değil, kesinlikle bakmaya değer bir şey. Örneğin IE, veri uri desteğini sınırlar. IE 8 boyutu 32KB ve IE 7 ile sınırlar ve daha azını desteklemez.
Darin Dimitrov

8
Chrome Sürüm 19.0.1084.46'da bu yöntem şu uyarıyı oluşturur: "Kaynak Belge olarak yorumlandı ancak MIME türü text / csv ile aktarıldı:" veri: metin / csv, alan1% 2Cfield2% 0Afoo% 2Cbar% 0Agoo% 2Cgai% 0A ". " Bir indirme tetiklenmez
Chris

2
Şimdi Chrome'da çalışıyor (v20 ve v21'e karşı test edildi) ancak IE9 değil (bu sadece jsFiddle olabilir, ancak bir şekilde şüpheliyim).
earcam

5
UTF-8'e dönüştüren kodunuz yoksa, doğru karakter seti neredeyse kesinlikle UTF-16'dır. JavaScript dahili olarak UTF-16 kullanır. Bir metin veya CSV dosyanız varsa, dizeyi '\ ufeff', UTF-16BE için Bayt Sırası İşareti ile başlatın ve metin editörleri ASCII olmayan karakterleri doğru bir şekilde okuyabilecektir.
larspars

14
Uygun dosya adı ve uzantısına sahip olmak ve işletim sisteminize bununla ne yapacağını söylemek için download = "txt.csv" özniteliğini eklemeniz yeterlidir.
elshnkhll

724

HTML5'e hazır tarayıcılar için basit çözüm ...

function download(filename, text) {
  var element = document.createElement('a');
  element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
  element.setAttribute('download', filename);

  element.style.display = 'none';
  document.body.appendChild(element);

  element.click();

  document.body.removeChild(element);
}
form * {
  display: block;
  margin: 10px;
}
<form onsubmit="download(this['name'].value, this['text'].value)">
  <input type="text" name="name" value="test.txt">
  <textarea name="text"></textarea>
  <input type="submit" value="Download">
</form>

kullanım

download('test.txt', 'Hello world!');


48
Evet, ancak downloadnitelik ile dosya adını ;-) belirtebilirsiniz
Matěj Pokorný


4
Chrome, txtuzantıyı yalnızca dosya adında bir uzantı sağlamazsanız ekler . Bunu yaparsanız download("data.json", data)beklendiği gibi çalışacaktır.
Carl Smith

6
Bu benim için Chrome (73.0.3683.86) ve Firefox'ta (66.0.2) çalıştı. Bu vermedi DEĞİL IE11 (11.379.17763.0) ve The Edge (44.17763.1.0) çalışırlar.
Sam

231

Yukarıdaki tüm çözümler tüm tarayıcılarda çalışmadı. Son olarak IE 10+, Firefox ve Chrome'da (ve jQuery veya başka bir kütüphane olmadan) çalışanlar:

save: function(filename, data) {
    var blob = new Blob([data], {type: 'text/csv'});
    if(window.navigator.msSaveOrOpenBlob) {
        window.navigator.msSaveBlob(blob, filename);
    }
    else{
        var elem = window.document.createElement('a');
        elem.href = window.URL.createObjectURL(blob);
        elem.download = filename;        
        document.body.appendChild(elem);
        elem.click();        
        document.body.removeChild(elem);
    }
}

Durumunuza bağlı olarak, kaldırdıktan sonra URL.revokeObjectURL öğesini de aramak isteyebilirsinizelem . URL.createObjectURL dokümanlarına göre :

CreateObjectURL () öğesini her aradığınızda, aynı nesne için zaten bir tane oluşturmuş olsanız bile yeni bir nesne URL'si oluşturulur. Bunların her birinin artık ihtiyacınız olmadığında URL.revokeObjectURL () çağrılarak serbest bırakılması gerekir. Belge kaldırıldığında tarayıcılar bunları otomatik olarak serbest bırakır; ancak, optimum performans ve bellek kullanımı için, bunları açıkça kaldırabileceğiniz güvenli zamanlar varsa, bunu yapmalısınız.


7
Milyonlarca kez teşekkürler. Burada listelenen tüm örnekleri denedim ve sadece bu herhangi bir tarayıcı ile çalışır. Bu kabul edilen cevap olmalı.
LEM

Chrome'da, bunun çalışması için öğeyi gövdeye eklemem gerekmiyordu.
JackMorrissey

1
AngularJS 1.x uygulamaları için, oluşturuldukça bir URL dizisi oluşturabilir ve bunları bileşenin $ onDestroy işlevinde temizleyebilirsiniz. Bu benim için harika çalışıyor.
Splaktar

1
Diğer yanıtlar Failed: network errorChrome'da ortaya çıktı. Bu iyi çalışıyor.
ardıç

4
Bu benim için Chrome (73.0.3683.86), Firefox (66.0.2), IE11 (11.379.17763.0) ve Edge'de (44.17763.1.0) çalıştı.
Sam

185

Yukarıdaki örneklerin tümü krom ve IE'de iyi çalışır, ancak Firefox'ta başarısız olur. Lütfen gövdeye bir çapa eklemeyi ve tıkladıktan sonra çıkarmayı düşünün.

var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(new Blob(['Test,Text'], {type: 'text/csv'}));
a.download = 'test.csv';

// Append anchor to body.
document.body.appendChild(a);
a.click();

// Remove anchor from body
document.body.removeChild(a);

4
Bununla birlikte , IE 10'da açık bir hata var (ve hala 11'de gördüm)a.click() , bloğun URL'sinin çapraz kökenli olduğunu düşündüğü için hatta "Erişim reddedildi" .
Matt,

@Matt data uri bazı tarayıcılarda çapraz kaynaklıdır. bildiğim kadarıyla, sadece msie'de değil, kromda da. javascript'i veri uri ile enjekte etmeye çalışarak test edebilirsiniz. Sitenin diğer yerlerine erişemez ...
inf3rno

7
"Yukarıdaki örneklerin tümü krom ve IE'de iyi çalışıyor, ancak Firefox'ta başarısız." Cevapların sırası zaman içinde değişebileceğinden, bunu yazdığınızda hangi cevapların sizin üzerinizde olduğu açık değildir. Firefox'ta hangi yaklaşımların işe yaramadığını tam olarak belirtebilir misiniz?
Kevin

120

Mutlu bir şekilde FileSaver.js kullanıyorum . Uyumluluğu oldukça iyidir (IE10 + ve diğer her şey) ve kullanımı çok basittir:

var blob = new Blob(["some text"], {
    type: "text/plain;charset=utf-8;",
});
saveAs(blob, "thing.txt");

Bu Chrome'da harika çalışıyor. Kullanıcının diskteki dosyanın konumunu belirtmesine nasıl izin verebilirim?
gregm

6
Vay canına, kullanımı kolay kütüphane için teşekkürler. Bu kolayca en iyi yanıt ve bu günlerde HTML <5 kullanan kişileri herhangi bir şekilde kimin umurunda?
notbad.jpeg

@gregm Bu eklentiyle yapabileceğinizden emin değilim.
Daniel Buckmaster

@gregm: İndirme konumunu mı kastediyorsunuz? Bu FileSaver.js ile ilgili değildir, tarayıcı yapılandırmanızı her indirmeden önce bir klasör isteyecek şekilde ayarlamanız veya oldukça yeni indirme özelliğini kullanmanız gerekir <a>.
CodeManX

1
Bu, IE 10+ tarayıcı ailesi için BÜYÜK bir çözümdür. IE henüz HTML 5 etiketini desteklemiyor ve bu sayfadaki diğer çözümler (ve aynı sorunu tartışan diğer SO sayfaları) benim için işe yaramıyordu. FileSaver ftw!
TMc

22

IE11 +, Firefox 25+ ve Chrome 30+ sürümlerinde aşağıdaki yöntem çalışır:

<a id="export" class="myButton" download="" href="#">export</a>
<script>
    function createDownloadLink(anchorSelector, str, fileName){
        if(window.navigator.msSaveOrOpenBlob) {
            var fileData = [str];
            blobObject = new Blob(fileData);
            $(anchorSelector).click(function(){
                window.navigator.msSaveOrOpenBlob(blobObject, fileName);
            });
        } else {
            var url = "data:text/plain;charset=utf-8," + encodeURIComponent(str);
            $(anchorSelector).attr("download", fileName);               
            $(anchorSelector).attr("href", url);
        }
    }

    $(function () {
        var str = "hi,file";
        createDownloadLink("#export",str,"file.txt");
    });

</script>

Bunu Eylemde görün: http://jsfiddle.net/Kg7eA/

Firefox ve Chrome, bir veri URI'sına giderek dosya oluşturmamıza izin verirken IE güvenlik açısından desteklemiyor.

Öte yandan IE, dosyaları oluşturmak ve indirmek için kullanılabilecek bir blob kaydetmek için API'ye sahiptir.


Sadece olayları eklemek için jquery kullandım (onclick ve onready) ve vanilya JS ile de yapabileceğiniz nitelikleri ayarladım. Çekirdek kısım (window.navigator.msSaveOrOpenBlob) jquery gerektirmez.
dinesh ygv

Veri uri yaklaşımı için hala boyut sınırlaması var, değil mi?
phil

msSaveOrOpenBlob burada eski olarak gösterilmektedir: developer.mozilla.org/en-US/docs/Web/API/Navigator/msSaveBlob
eflat

13

Bu çözüm doğrudan tiddlywiki'nin (tiddlywiki.com) github deposundan çıkarılır. Neredeyse tüm tarayıcılarda tiddlywiki kullandım ve bir cazibe gibi çalışıyor:

function(filename,text){
    // Set up the link
    var link = document.createElement("a");
    link.setAttribute("target","_blank");
    if(Blob !== undefined) {
        var blob = new Blob([text], {type: "text/plain"});
        link.setAttribute("href", URL.createObjectURL(blob));
    } else {
        link.setAttribute("href","data:text/plain," + encodeURIComponent(text));
    }
    link.setAttribute("download",filename);
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}

Github repo: Tasarruf modülünü indirin


Chrome'da çok güzel çalışıyor, ancak Firefox'ta çalışmıyor. Bir dosya oluşturur ve indirir, ancak dosya boştur. İçerik yok. Neden herhangi bir fikir? IE'de test
etmedim

2
Fonksiyon adı yok olduğunu, bu benim en sevdiğim hariç
Yoraco Gonzales

11

IE10 üzerinde çalışan bir çözüm: (Bir csv dosyasına ihtiyacım vardı, ancak türü ve dosya adını txt olarak değiştirmek yeterli)

var csvContent=data; //here we load our csv data 
var blob = new Blob([csvContent],{
    type: "text/csv;charset=utf-8;"
});

navigator.msSaveBlob(blob, "filename.csv")

1
Ludovic'in yanıtı bu büyük artı diğer tarayıcılar için destek içeriyor.
Dan Dascalescu

10

Bir dizeyi indirilmek üzere dönüştürmek istiyorsanız, bunu jQuery kullanarak deneyebilirsiniz.

$('a.download').attr('href', 'data:application/csv;charset=utf-8,' + encodeURI(data));

1
EncodeURI ile Scape verileri, yorum yapmadan önce burada önerdiğim gibi gerekebilir: stackoverflow.com/a/32441536/4928558
atfornes

10

Paket js-file-indir gelen github.com/kennethjiang/js-file-download tarayıcı desteği için kolları uç örnekleri:

Bu sayfada bahsedilen teknikleri nasıl kullandığını görmek için kaynağı görüntüleyin .

Kurulum

yarn add js-file-download
npm install --save js-file-download

kullanım

import fileDownload from 'js-file-download'

// fileDownload(data, filename, mime)
// mime is optional

fileDownload(data, 'filename.csv', 'text/csv')

1
Teşekkürler - yeni test edildi - Windows'ta Firefox, Chrome ve Edge ile çalışıyor
Brian Burns

8

Daha önce de belirtildiği gibi, dosya koruyucu , istemci tarafındaki dosyalarla çalışmak için harika bir pakettir. Ancak, büyük dosyalarla iyi sonuç vermez. StreamSaver.js , büyük dosyaları işleyebilen alternatif bir çözümdür (FileServer.js'de gösterilmiştir):

const fileStream = streamSaver.createWriteStream('filename.txt', size);
const writer = fileStream.getWriter();
for(var i = 0; i < 100; i++){
    var uint8array = new TextEncoder("utf-8").encode("Plain Text");
    writer.write(uint8array);
}
writer.close()

1
Metin Kodlayıcı şu anda son derece deneysel, kaçınmayı (veya çoklu doldurmayı) öneririm.
Brandon.Blanchard

7
var element = document.createElement('a');
element.setAttribute('href', 'data:text/text;charset=utf-8,' +      encodeURI(data));
element.setAttribute('download', "fileName.txt");
element.click();

1
Bu yaklaşım ve bir Blob oluşturma arasındaki farklar nelerdir?
Dan Dascalescu


5

@Rick cevabına dayanarak gerçekten yardımcı oldu.

dataBu şekilde paylaşmak istiyorsanız dizeyi kaydırmanız gerekir:

$('a.download').attr('href', 'data:application/csv;charset=utf-8,'+ encodeURI(data));

`` StackOverflow'daki mevcut düşük itibarım nedeniyle @ Rick'in cevabı hakkında yorum yapamıyorum.

Bir düzenleme önerisi paylaşıldı ve reddedildi.


1
Öneriyi kabul edemedim. Garip ... Kodu güncelledim.
Rick

4

Biz kullanabilirsiniz URL özellikle api URL.createObjectURL () ve Blob hemen her şeyi kodlamak ve indirmek için api .

İndirmeniz küçükse, bu iyi çalışır:

document.body.innerHTML += 
`<a id="download" download="PATTERN.json" href="${URL.createObjectURL(new Blob([JSON.stringify("HELLO WORLD", null, 2)]))}"> Click me</a>`
download.click()
download.outerHTML = ""


İndirme işleminiz çok büyükse, DOM kullanmak yerine daha iyi bir yol, indirme parametreleriyle bir bağlantı öğesi oluşturmak ve bir tıklamayı tetiklemektir.

Bağlantı öğesinin belgeye eklenmediğine, ancak tıklamanın yine de işe yaradığına dikkat edin! Bu, yüzlerce Mo'nın bu şekilde indirilmesini sağlamak mümkündür.

const stack = {
 some: "stuffs",
 alot: "of them!"
}

BUTTONDOWNLOAD.onclick = (function(){
  let j = document.createElement("a")
  j.id = "download"
  j.download = "stack_"+Date.now()+".json"
  j.href = URL.createObjectURL(new Blob([JSON.stringify(stack, null, 2)]))
  j.click()
})  
<button id="BUTTONDOWNLOAD">DOWNLOAD!</button>


Bonus! Herhangi bir döngüsel nesneyi indirin , hatalardan kaçının:

TypeError: döngüsel nesne değeri (Firefox) TypeError: Dönüştürme

JSON (Chrome ve Opera) TypeError: Dairesel şeklinde dairesel yapı

değer bağımsız değişkenindeki başvuru desteklenmiyor (Edge)

Https://github.com/douglascrockford/JSON-js/blob/master/cycle.js kullanma

Bu örnekte, documentnesneyi json olarak indirme .

/* JSON.decycle */
if(typeof JSON.decycle!=="function"){JSON.decycle=function decycle(object,replacer){"use strict";var objects=new WeakMap();return(function derez(value,path){var old_path;var nu;if(replacer!==undefined){value=replacer(value)}
if(typeof value==="object"&&value!==null&&!(value instanceof Boolean)&&!(value instanceof Date)&&!(value instanceof Number)&&!(value instanceof RegExp)&&!(value instanceof String)){old_path=objects.get(value);if(old_path!==undefined){return{$ref:old_path}}
objects.set(value,path);if(Array.isArray(value)){nu=[];value.forEach(function(element,i){nu[i]=derez(element,path+"["+i+"]")})}else{nu={};Object.keys(value).forEach(function(name){nu[name]=derez(value[name],path+"["+JSON.stringify(name)+"]")})}
return nu}
return value}(object,"$"))}}


document.body.innerHTML += 
`<a id="download" download="PATTERN.json" href="${URL.createObjectURL(new Blob([JSON.stringify(JSON.decycle(document), null, 2)]))}"></a>`
download.click()



2

Aşağıdaki fonksiyon işe yaradı.

 private createDownloadableCsvFile(fileName, content) {
   let link = document.createElement("a");
   link.download = fileName;
   link.href = `data:application/octet-stream,${content}`;
   return link;
 }

dosyayı yeni bir sekmede açıp fileName'i atandı, ancak indirmiyor, sadece bir sekmede açabiliyor musunuz?
Carl Kroeger Ihl

1

Aşağıdaki yöntem IE10 +, Edge, Opera, FF ve Chrome'da çalışır:

const saveDownloadedData = (fileName, data) => {
    if(~navigator.userAgent.indexOf('MSIE') || ~navigator.appVersion.indexOf('Trident/')) { /* IE9-11 */
        const blob = new Blob([data], { type: 'text/csv;charset=utf-8;' });
        navigator.msSaveBlob(blob, fileName);
    } else {
        const link = document.createElement('a')
        link.setAttribute('target', '_blank');
        if(Blob !== undefined) {
            const blob = new Blob([data], { type: 'text/plain' });
            link.setAttribute('href', URL.createObjectURL(blob));
        } else {
            link.setAttribute('href', 'data:text/plain,' + encodeURIComponent(data));
        }

        ~window.navigator.userAgent.indexOf('Edge')
            && (fileName = fileName.replace(/[&\/\\#,+$~%.'':*?<>{}]/g, '_')); /* Edge */

        link.setAttribute('download', fileName);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    }
}

Yani, fonksiyonu çağırmanız yeterlidir:

saveDownloadedData('test.txt', 'Lorem ipsum');

0

Benim için bu mükemmel çalıştı, aynı dosya adı ve uzantısı indiriliyor

<a href={"data:application/octet-stream;charset=utf-16le;base64," + file64 } download={title} >{title}</a>

'title', uzantılı dosya adıdır, yani sample.pdf,waterfall.jpg vb ..

'file64' böyle bir şey temel64 içeriğidir, yani, Ww6IDEwNDAsIFNsaWRpbmdTY2FsZUdyb3VwOiAiR3JvdXAgQiIsIE1lZGljYWxWaXNpdEZsYXRGZWU6IDM1LCBEZW50YWxQYXltZW50UGVyY2VudGFnZTogMjUsIFByb2NlZHVyZVBlcmNlbnQ6IDcwLKCFfSB7IkdyYW5kVG90YWwiOjEwNDAsIlNsaWRpbmdTY2FsZUdyb3VwIjoiR3JvdXAgQiIsIk1lZGljYWxWaXNpdEZsYXRGZWUiOjM1LCJEZW50YWxQYXltZW50UGVyY2VudGFnZSI6MjUsIlByb2NlZHVyZVBlcmNlbnQiOjcwLCJDcmVhdGVkX0J5IjoiVGVycnkgTGVlIiwiUGF0aWVudExpc3QiOlt7IlBhdGllbnRO


0

Bir <a></a>etiket kullanmak ve sonra ayarlayın href='path'. Daha sonra, <a>öğelerin arasına bir görüntü yerleştirin, böylece görmek için bir görselim olsun. İsterseniz, değiştirilecek bir işlev oluşturabilirsiniz.href yalnızca aynı bağlantı olmayacak, aynı zamanda dinamik .

Javascript ile erişmek istiyorsanız <a>etikete bir idde verin .

HTML Sürümünden Başlayarak:

<a href="mp3/tupac_shakur-how-do-you-want-it.mp3" download id="mp3Anchor">
     <img src="some image that you want" alt="some description" width="100px" height="100px" />
</a>

Şimdi JavaScript ile:

*Create a small json file*;

const array = [
     "mp3/tupac_shakur-how-do-you-want-it.mp3",
     "mp3/spice_one-born-to-die.mp3",
     "mp3/captain_planet_theme_song.mp3",
     "mp3/tenchu-intro.mp3",
     "mp3/resident_evil_nemesis-intro-theme.mp3"
];

//load this function on window
window.addEventListener("load", downloadList);

//now create a function that will change the content of the href with every click
function downloadList() {
     var changeHref=document.getElementById("mp3Anchor");

     var j = -1;

     changeHref.addEventListener("click", ()=> {

           if(j < array.length-1) {
               j +=1;
               changeHref.href=""+array[j];
          }
           else {
               alert("No more content to download");
          }
}

-22

Dosya metin verileri içeriyorsa, kullandığım bir teknik, metni bir textarea öğesine yerleştirmek ve kullanıcının onu seçmesini sağlamaktır (textarea'yı ve ardından ctrl-A'yı tıklayın), ardından kopyalayıp ardından bir metin düzenleyicisine yapıştırarak yapıştırmaktır.


30
Bunu düşünmüştüm, ama kullanım kolaylığı açısından bu felaket. Ayrıca, dosyanın bir CSV uzantısıyla kaydedilmesi gerekir. Bunu kullanıcılarınıza anlatmayı deneyin.
Joseph Silber
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.