HTML5 / JavaScriptを使用してファイルを生成および保存する


317

私は最近WebGLをいじっていて、Colladaリーダーを動かしてもらいました。問題は、それがかなり遅い(Colladaは非常に冗長な形式です)ので、ファイルをより使いやすい形式(おそらくJSON)に変換し始めます。JavaScriptでファイルを解析するためのコードがすでにあるので、それをエクスポーターとして使用することもできます。問題は節約です。

これで、ファイルを解析して結果をサーバーに送信し、ブラウザーにサーバーからのダウンロードとしてファイルを要求するようにできることがわかりました。しかし実際には、サーバーはこの特定のプロセスとは何の関係もないので、なぜそれを関与させるのでしょうか。目的のファイルの内容はすでにメモリにあります。純粋なJavaScriptを使用してダウンロードをユーザーに提示する方法はありますか?(私はそれを疑っていますが、尋ねるかもしれません...)

そして明確にするために:私はユーザーの知識なしにファイルシステムにアクセスしようとはしていません!ユーザーはファイルを(おそらくドラッグアンドドロップで)提供し、スクリプトはファイルをメモリ内で変換し、結果をダウンロードするように求められます。ブラウザに関する限り、これらはすべて「安全な」アクティビティである必要があります。

[編集]:私はそれを前に述べなかったので、「フラッシュ」と答えたポスターは十分に有効ですが、私がしていることの一部は、純粋なHTML5で何ができるかを強調する試みです...私の場合はすぐに。(「実際の」Webアプリを実行しているすべての人にとって、これは完全に有効な答えですが)サーバーに関与したくない限り、私は運が悪いように見えます。とにかくありがとう!


8
受け入れられた回答を変更することを検討することができます。今は純粋にHTMLの方法があるようです
Pekka

回答:


256

OK、data:URIを作成すると、間違いなく私にとってうまくいきます。MatthewとDennksterがそのオプションを指摘しているおかげです!基本的には次のようにします。

1)すべてのコンテンツを「コンテンツ」と呼ばれる文字列に取得します(たとえば、最初にコンテンツを作成するか、すでに作成されているページのタグのinnerHTMLを読み取ることによって)。

2)データURIを作成します。

uriContent = "data:application/octet-stream," + encodeURIComponent(content);

ブラウザの種類などによっては長さに制限がありますが、たとえばFirefox 3.6.12は少なくとも256kまで機能します。代わりに、encodeURIComponentを使用してBase64でエンコードすると、効率が上がる可能性がありますが、私にとっては問題ありませんでした。

3)新しいウィンドウを開いて、このURIに「リダイレクト」すると、JavaScriptで生成されたページのダウンロード場所の入力を求められます。

newWindow = window.open(uriContent, 'neuesDokument');

それでおしまい。


34
ブロックされる可能性のあるポップアップの使用を避けたい場合はlocation.href、コンテンツにを設定できます。location.href = uriContent
Alex Turpin

12
こんにちは私はこれを試しましたが、それはファイルを.partファイルとしてダウンロードします。ファイルタイプを設定するにはどうすればよいですか?
SedatBaşar、2011年

6
@SedatBaşarデータURIはファイル名の設定をサポートしていないため、ブラウザはMIMEタイプを使用して適切な拡張子を設定する必要があります。ただし、ブラウザでMIMEタイプをレンダリングできる場合は、ダウンロードされませんが、表示されます。これを行う方法は他にもいくつかありますが、IE <10ではどちらも機能しません。
panzi

7
IEは実際にはデータURIをサポートしておらず、Firefoxはランダムな名前でファイルを保存しているようです。
ニランド2013

25
私たちはこれを必要以上に難しくしていると思います。このページでJSコンソールを開いて配置するlocation.href = "data:application/octet-stream," + encodeURIComponent(jQuery('#answer-4551467 .post-text').first().text());と、@Nøkの回答の内容がファイルに保存されます。テストするIEはありませんが、webkitで動作します。
Bruno Bronosky 2013年

278

HTML5対応ブラウザ向けのシンプルなソリューション...

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

    if (document.createEvent) {
        var event = document.createEvent('MouseEvents');
        event.initEvent('click', true, true);
        pom.dispatchEvent(event);
    }
    else {
        pom.click();
    }
}

使用法

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

2
あなたがダウンロードしたいソースURLを知っているなら、これが最善の解決策です!
sidonaldson 2013

31
ファイル名を設定する機能はこれを勝者にします。
Omn

9
この方法は、数日前に受け取った最新のアップデート(35.0.1916.114 m)までChromeで機能しました。今では部分的に動作するファイル名と拡張子もはや作品(それは常に名前に関係なく、渡されるもののファイルdownload.txt。)
Sevin7

6
Chrom 42.0.2311.90を使用していますが、これは期待されるファイル名で機能します。
Saurabh Kumar

4
含めることができるデータの量に制限がある場合はどうなりますか?
Kaspar Lee

80

HTML5はwindow.saveAs(blob, filename)メソッドを定義しました。現在、どのブラウザでもサポートされていません。しかし、FileSaver.jsと呼ばれる互換性ライブラリがあり、この機能を最新のほとんどのブラウザ(Internet Explorer 10以降を含む)に追加しています。Internet Explorer 10は、FileSaver.jsでInternet Explorerをサポートするために使用されるnavigator.msSaveBlob(blob, filename)メソッド(MSDN)をサポートしています。

私が書いたブログの投稿をこの問題の詳細と。


1
ポップアップのブロックについてはどうですか?このライブラリの動作は、@Nøkのソリューションに似ています。Firefoxのプレーンテキストを新しく開きます。Chromeのみが保存を試みますが、拡張子が変更されます(拡張子のないdotfileが必要です)。
ciembor 2012

1
@ciembor(object url +)download属性のバリアント(私がchromeで使用)を使用すると、ファイル名を設定できます。それは私にとってクロームで動作します。
panzi

1
@ciembor ahaとポップアップが直接クリックされた場合、ポップアップはブロックされません。
panzi

6
FileSaver.jsがIEをサポート
Eli Grey

15
W3Cによると:このドキュメントの作業は中止されており、参照または実装の基礎として使用することはできません。
WaiKit Kung、2014

48

大きなファイルを保存する

長いデータURIは、ブラウザーでパフォーマンスの問題を引き起こす可能性があります。クライアント側で生成されたファイルを保存する別のオプションは、それらのコンテンツをBlob(またはFile)オブジェクトに入れ、を使用してダウンロードリンクを作成することURL.createObjectURL(blob)です。これは、BLOBのコンテンツを取得するために使用できるURLを返します。blobはURL.revokeObjectURL()、URLで呼び出されるか、それを作成したドキュメントが閉じられるまで、ブラウザー内に保存されます。ほとんどのWebブラウザーはオブジェクトURLサポートしています。OperaMiniはそれらをサポートしない唯一のブラウザーです。

ダウンロードを強制する

データがテキストまたは画像の場合、ブラウザはファイルをディスクに保存する代わりに開くことができます。リンクをクリックするとファイルがダウンロードされるようにするには、download属性を使用できます。ただし、すべてのWebブラウザーがダウンロード属性をサポートしているわけではありません。もう1つのオプションはapplication/octet-stream、ファイルのMIMEタイプとして使用することですが、これによりファイルがバイナリBLOBとして表示され、ファイル名を指定できない場合や指定できない場合は特にユーザーに不便になります。「「名前を付けて保存」ポップアップをテキストリンクで開くことを強制するHTMLPDFのクリック」も参照してください。

ファイル名の指定

BlobがFileコンストラクターで作成されている場合は、ファイル名も設定できますが、Fileコンストラクターをサポートしているのはごく一部のWebブラウザー(ChromeおよびFirefoxを含む)のみです。ファイル名はdownload属性の引数として指定することもできますが、これにはセキュリティ上の考慮事項がたくさんあります。Internet Explorer 10および11には、ファイル名を指定するための独自のメソッドmsSaveBlobが用意されています。

コード例

var file;
var data = [];
data.push("This is a test\n");
data.push("Of creating a file\n");
data.push("In a browser\n");
var properties = {type: 'text/plain'}; // Specify the file's mime-type.
try {
  // Specify the filename using the File constructor, but ...
  file = new File(data, "file.txt", properties);
} catch (e) {
  // ... fall back to the Blob constructor if that isn't supported.
  file = new Blob(data, properties);
}
var url = URL.createObjectURL(file);
document.getElementById('link').href = url;
<a id="link" target="_blank" download="file.txt">Download</a>


1
ファイルを保存するフォルダ(ディレクトリ)を指定するダイアログ(ポップアップ)を表示できますか?
カルバン、

@Calvin:ダウンロードを強制してファイル名を提供する方法を説明するために回答を更新しました。IEの場合msSaveBlob、「名前を付けて保存」ダイアログを開くのに使用できると思います。その他のブラウザの場合、ダウンロードリンクのコンテキストメニューから[名前を付けて保存]を手動で選択するのが唯一のオプションです。
bcmpinc 2016

1
@ Jek-fdrv:SafariではBlob-urlのみが機能します。ダウンロード属性とFileコンストラクターはSafariではサポートされていないため、ダウンロードを強制することはできません。つまり、ブロブはおそらくブラウザー自体で開かれ、ファイル名を指定することはできません。上記の例では、リンクのコンテキストメニューを使用して、Safariでファイルをダウンロードできるはずです。
bcmpinc 2016


これは非常に役立つ有益な回答です。私のような人々を助けるかもしれないもう一つのこと:を設定した後document.getElementById('link').href = url;、あなたのコードは要素の.click()メソッドを使用して先に進み、リンクを起動することができます。
LarsH 2018

36
function download(content, filename, contentType)
{
    if(!contentType) contentType = 'application/octet-stream';
        var a = document.createElement('a');
        var blob = new Blob([content], {'type':contentType});
        a.href = window.URL.createObjectURL(blob);
        a.download = filename;
        a.click();
}

3
contentTypeの効果は何ですか?何に使うの?
Uri

3
@MatějPokornýの回答とは異なり、これは最新のChromeでも正常に機能します。ありがとう。
Alexander Amelkin 14年

2
これはFF36やIE11では機能しません。MatějPokornýが提案a.clickするコードを使用しdocument.createEvent()て置き換えると、IEではなくFFで機能します。Chromeを試したことはありません。
Peter Hull

25

これを行うためのFlashベースのJavaScriptインターフェースであるDoug NeinerのDownloadifyをご覧ください。

Downloadifyは小さなJavaScript + Flashライブラリで、サーバーとの対話なしで、ブラウザーでファイルをオンザフライで生成および保存できます。


6
ほとんどの人にとって、これはおそらく彼らが必要とする答えです。したがって、(上記で説明したように)私の特定の要件を満たしていない場合でも、これを承認済みの回答としてマークします。
Toji

2
@Tojiああ、なるほど。たぶん、HTML 5バナーの下で再度尋ねて、言い換えて、それに応じてタグを付けますか?それは、その特定の分野を知っているユーザーを引き付ける可能性が高いでしょう(今のところまだ比較的小さな群衆だと思います)。HTML 5で実行できると確信していますが、方法はわかりません。
Pekka、

1
downloadify.info Downloadifyドメインは、転送/購入されて、そうであれば、新しい場所はありますか?現在のサイトは、与えられた答えとはまったく無関係のようです。
Christos Hayward、

17
これは、HTML5使用...-タイトル付きの質問には答えません。
Ixx

5
@Ixxは公平に言えば、回答が投稿された後に追加されたものです。それでも、あなたは正しい。以下の答えは受け入れられるべきです
Pekka

17

シンプルなソリューション!

<a download="My-FileName.txt" href="data:application/octet-stream,HELLO-WORLDDDDDDDD">Click here</a>

すべての最新ブラウザで動作します。


こんにちは、window.openまたは別のJavaScript関数を使用して「ダウンロード」属性の動作を指定する方法を知っていますか?
yucer

2
@yucerには、ダウンロード属性(またはその問題に関する属性)はありませんwindow.open()。それは無関係です。このメソッドを使用して強制的に.click()オンにすることもできますが.click()、要素を本文に添付する前に呼び出すと、Firefoxが気に入らないのでタイミングを監視します。
Brad

しかし残念なことに、すべてのスペースが削除されます。私の場合、SVGファイルのソースコードをダウンロードしたいので、それは本当に悪いことです。
frankenapps 2017年

あなたはencodeURIComponentで(コンテンツ)を使用する場合スペースが保存されている
マイクRedrobe

Firefoxでは名前を選択できませんが、
Chromeは

10

データURIを生成できます。ただし、ブラウザ固有の制限があります。


1
これは面白い。機会があればもっと調べます。ありがとう!
Toji

@quantumpotato、実際にURLを生成することは、説明が少し難しいです。すべての要点はRFC 2397にあります。このようなツールを使用してテストできます。次に、実際のアプリで、データURIまたは言語のBase 64ライブラリを検索できます。それが見つからない場合は、フォローアップの質問をしてください。ブラウザー固有の制限の一部は、ウィキペディアの記事に記載されています。たとえば、IEは長​​さとタイプを制限します(例:text / htmlではありません)。
Matthew Flaschen、2012年

データURLの生成はそれほど難しいことではありません。"data:text/plain;charset=UTF-8,"+encodeURIComponent(text)ただし、IEはデータURLのサイズを使用できない量に制限しているため、window.open(...)またはiframe などではサポートされていません(私はそう思います)。
パンジ

@panzi、それもそれほど簡単ではありません。1つには、ColladaやJSONのどちらにも適切なMIMEタイプではありません。
Matthew Flaschen 2013年

あまり有益ではない答え。言及する場合は、ブラウザの仕様を追加してください。
T.Todua

10

私はFileSaver(https://github.com/eligrey/FileSaver.js)を使用しましたが、問題なく動作します。
たとえば、ページに表示されたログをエクスポートするためにこの機能を実行しました。
Blobのインスタンス化のために配列を渡す必要があるので、これを正しい方法で記述しなかっただけかもしれませんが、私にとってはうまくいきます。
念のため、置換に注意してください。これは、このグローバルにする構文です。それ以外の場合は、最初に遭遇したものだけを置き換えます。

exportLogs : function(){
    var array = new Array();

    var str = $('#logs').html();
    array[0] = str.replace(/<br>/g, '\n\t');

    var blob = new Blob(array, {type: "text/plain;charset=utf-8"});
    saveAs(blob, "example.log");
}

2
FileSaverは素晴らしいです。IE10以前の関数preIE10SaveAsのIE Shimは次のとおりです:(filename、filecontent、mimetype){var w = window.open(); var doc = w.document; doc.open(mimetype、 'replace'); doc.charset = "utf-8"; doc.write(filecontent); doc.close(); doc.execCommand( "SaveAs"、null、filename); }
aqm 2014年

@aqmのシムに関する警告:スクリプトタグを実行します。
GameFreak

また、配置することが望ましいかもしれないw.close();execCommand
GameFreak

8

私には2つのシンプルなアプローチがうまくいきました。まず、既にクリックされたa要素を使用して、ダウンロードデータを挿入します。a次に、ダウンロードデータを含む要素を生成し、a.click()再度実行して削除します。ただし、2番目の方法は、ユーザーのクリックアクションによって呼び出された場合にのみ機能します。(一部)click()ロード時やタイムアウト(setTimeout)後のトリガーなど、他のコンテキストからのブラウザーブロック。

<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="UTF-8">
    <script type="text/javascript">
      function linkDownload(a, filename, content) {
        contentType =  'data:application/octet-stream,';
        uriContent = contentType + encodeURIComponent(content);
        a.setAttribute('href', uriContent);
        a.setAttribute('download', filename);
      }
      function download(filename, content) {
        var a = document.createElement('a');
        linkDownload(a, filename, content);
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
      }
    </script>
   </head>
  <body>
    <a href="#" onclick="linkDownload(this, 'test.txt', 'Hello World!');">download</a>
    <button onclick="download('test.txt', 'Hello World!');">download</button>
  </body>
</html>

2
aドキュメントに挿入し(おそらくで"display:none")、クリックして削除することもできます。
Teepeemm、2014年

1
これは、最新のieやsafariのようにダウンロード属性がサポートされていないブラウザーで機能します。caniuse.com/#feat
Muhammad

1
ワインの下でSafari 5.0をテストしたところです。最初のバージョンは機能しますが、2つ目は機能しません。IE 8(ワイン)もテストしましたが、動作しません。
マイケル2014年

6

Mathewが提案したデータURIメソッドへのリンクは次のとおりです。これはサファリで機能しましたが、ファイルタイプを設定できなかったためうまくいきませんでした。「不明」として保存され、後で再びそこに移動して、順序を変更する必要がありますファイルを表示するには...

http://www.nihilogic.dk/labs/canvas2image/


4

localStorageを使用できます。これはHtml5のCookieに相当します。ChromeとFirefoxでは動作するようですが、Firefoxでは動作します。サーバーにアップロードする必要がありました。つまり、私の自宅のコンピューターで直接テストすることはできませんでした。

HTML5の例を作成しています。http://faculty.purchase.edu/jeanine.meyer/html5/html5explain.htmlに移動し て、迷路にスクロールします。迷路を再構築するための情報は、localStorageを使用して保存されます。

この記事を読み、xmlファイルを読み込んで操作するためのHTML5 JavaScriptを探しました。古いhtmlやJavaScriptと同じですか????


4

試す

let a = document.createElement('a');
a.href = "data:application/octet-stream,"+encodeURIComponent('"My DATA"');
a.download = 'myFile.json';
a.click(); // we not add 'a' to DOM so no need to remove

バイナリデータをダウンロードしたい場合はこちらをご覧ください

更新

2020.06.14 Chromeを83.0以上にアップグレードすると、SOスニペット停止は機能します(サンドボックスのセキュリティ制限により)-しかし、JSFiddleバージョンは機能します- ここで


3

前述のように、File APIは、FileWriterおよびFileSystem API とともに、ブラウザのタブ/ウィンドウのコンテキストからクライアントのマシンにファイルを格納するために使用できます。

ただし、次の2つのAPIに関連するいくつかの注意事項があります。

  • 現在、APIの実装はChromiumベースのブラウザー(ChromeおよびOpera)にのみ存在します
  • どちらのAPIも2014年4月24日にW3C標準トラックから外され、現在は独自仕様です
  • 将来的にはブラウザーの実装から(現在は独自仕様の)APIを削除する可能性があります
  • サンドボックス(ファイルは効果を生み出すことができないそのディスク外側の位置)をAPIで作成されたファイルを格納するために使用されます
  • 仮想ファイルシステムに使用される(必ずしもそれがブラウザ内からアクセスする場合と同じ形式でディスク上に存在しないディレクトリ構造)APIで作成されたファイルを表します

これを行うために、APIを直接または間接的に組み合わせて使用​​する簡単な例を次に示します。

焼き菓子 *

bakedGoods.get({
        data: ["testFile"],
        storageTypes: ["fileSystem"],
        options: {fileSystem:{storageType: Window.PERSISTENT}},
        complete: function(resultDataObj, byStorageTypeErrorObj){}
});

未加工のFile、FileWriter、およびFileSystem APIの使用

function onQuotaRequestSuccess(grantedQuota)
{

    function saveFile(directoryEntry)
    {

        function createFileWriter(fileEntry)
        {

            function write(fileWriter)
            {
                var dataBlob = new Blob(["Hello world!"], {type: "text/plain"});
                fileWriter.write(dataBlob);              
            }

            fileEntry.createWriter(write);
        }

        directoryEntry.getFile(
            "testFile", 
            {create: true, exclusive: true},
            createFileWriter
        );
    }

    requestFileSystem(Window.PERSISTENT, grantedQuota, saveFile);
}

var desiredQuota = 1024 * 1024 * 1024;
var quotaManagementObj = navigator.webkitPersistentStorage;
quotaManagementObj.requestQuota(desiredQuota, onQuotaRequestSuccess);

FileSystem APIとFileWriter APIはもはや標準化の過程にありませんが、私の意見では、これらの使用は正当化できる場合があります。

  • 実装されていないブラウザーベンダーからの新たな関心により、ブラウザーベンダーはすぐに再配置されます
  • 実装(Chromiumベース)ブラウザーの市場浸透率は高い
  • Google(Chromiumへの主な貢献者)は、APIにサポート終了日を示していません

ただし、「一部のケース」があなた自身のケースを包含するかどうかは、あなたが決めることです。

* BakedGoodsは、この男以外の誰もがここで管理しています:)


2

このスレッドは、バイナリファイルを生成し、指定されたファイルをダウンロードするように求める方法を理解するのに非常に役立ちました。

私にとっての最初のステップは、保存していたデータからバイナリBLOBを生成することでした。単一のバイナリタイプに対してこれを行うためのサンプルはたくさんあります。私の場合、配列として渡してBLOBを作成できる複数のタイプのバイナリ形式があります。

saveAnimation: function() {

    var device = this.Device;
    var maxRow = ChromaAnimation.getMaxRow(device);
    var maxColumn = ChromaAnimation.getMaxColumn(device);
    var frames = this.Frames;
    var frameCount = frames.length;

    var writeArrays = [];


    var writeArray = new Uint32Array(1);
    var version = 1;
    writeArray[0] = version;
    writeArrays.push(writeArray.buffer);
    //console.log('version:', version);


    var writeArray = new Uint8Array(1);
    var deviceType = this.DeviceType;
    writeArray[0] = deviceType;
    writeArrays.push(writeArray.buffer);
    //console.log('deviceType:', deviceType);


    var writeArray = new Uint8Array(1);
    writeArray[0] = device;
    writeArrays.push(writeArray.buffer);
    //console.log('device:', device);


    var writeArray = new Uint32Array(1);
    writeArray[0] = frameCount;
    writeArrays.push(writeArray.buffer);
    //console.log('frameCount:', frameCount);

    for (var index = 0; index < frameCount; ++index) {

      var frame = frames[index];

      var writeArray = new Float32Array(1);
      var duration = frame.Duration;
      if (duration < 0.033) {
        duration = 0.033;
      }
      writeArray[0] = duration;
      writeArrays.push(writeArray.buffer);

      //console.log('Frame', index, 'duration', duration);

      var writeArray = new Uint32Array(maxRow * maxColumn);
      for (var i = 0; i < maxRow; ++i) {
        for (var j = 0; j < maxColumn; ++j) {
          var color = frame.Colors[i][j];
          writeArray[i * maxColumn + j] = color;
        }
      }
      writeArrays.push(writeArray.buffer);
    }

    var blob = new Blob(writeArrays, {type: 'application/octet-stream'});

    return blob;
}

次のステップは、事前定義された名前でこのbl​​obをダウンロードするようにユーザーにプロンプ​​トを表示させることです。

必要なのは、HTML5に追加した名前付きリンクだけで、これを再利用して初期ファイル名を変更できます。リンクを表示する必要がないため、非表示のままにしました。

<a id="lnkDownload" style="display: none" download="client.chroma" href="" target="_blank"></a>

最後のステップは、ユーザーにファイルをダウンロードするように促すことです。

var data = animation.saveAnimation();
var uriContent = URL.createObjectURL(data);
var lnkDownload = document.getElementById('lnkDownload');
lnkDownload.download = 'theDefaultFileName.extension';
lnkDownload.href = uriContent;
lnkDownload.click();

1

ファイルをZIPとしてエクスポートするチュートリアルは次のとおりです。

始める前に、ファイルを保存するためのライブラリがあり、ライブラリの名前はfileSaver.jsです。このライブラリはここにあります。さあ始めましょう、今、必要なライブラリを含めます:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.4/jszip.min.js"  type="text/javascript"></script>
<script type="text/javascript" src="https://fastcdn.org/FileSaver.js/1.1.20151003/FileSaver.js" ></script>

次に、このコードをコピーします。このコードは、Hello Worldというコンテンツを含むhello.txtファイルを含むzipファイルをダウンロードします。すべてがうまくいくと、ファイルがダウンロードされます。

<script type="text/javascript">
    var zip = new JSZip();
    zip.file("Hello.txt", "Hello World\n");
    zip.generateAsync({type:"blob"})
    .then(function(content) {
        // see FileSaver.js
        saveAs(content, "file.zip");
    });
</script>

これにより、file.zipというファイルがダウンロードされます。詳しくは、http//www.wapgee.com/story/248/guide-to-create-zip-files-using-javascript-by-using-jszip-libraryをご覧ください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.