0
0

html で文字列をローカルに保存する【名前を付けて保存】

Last updated at Posted at 2024-09-18

今回は html で
文字列をテキストファイルで
ローカルに保存する場合の
ソースを公開してみようと思います。

名前を付けて保存機能があるブラウザでは
名前を付けて保存で保存します。
Chrome や Edge が該当します。

名前を付けて保存機能が無いブラウザでは
直接ダウンロードします。


今回のソースコード

ローカルに保存するコード
let fileName = document.getElementById('fileNameTextBox').value;
const pattern = /[\\\/:\*\?\"<>\|]/;

if(pattern.test(fileName)){
	window.alert('ファイル名に使用できない文字が含まれています。');
}
else {
	const save = ""; //この save 変数に保存する文字列を入れて下さい。
	if(fileName == ''){
		var dt = new Date();
		var y = '' + dt.getFullYear();
		var m = ('00' + (dt.getMonth()+1)).slice(-2);
		var d = ('00' + dt.getDate()).slice(-2);
		var hour = ('00' + dt.getHours()).slice(-2);
		var minute = ('00' + dt.getMinutes()).slice(-2);
		var second = ('00' + dt.getSeconds()).slice(-2);
		fileName = "〇〇バックアップ" + (y + m + d + hour + minute + second); //〇〇にはサイト名を入れて下さい。
	}
	if(typeof window.showSaveFilePicker != 'function'){
		const blob = new Blob([save],{type:"text/plain"});
		let downloadTag = document.createElement('a');
		downloadTag.href = URL.createObjectURL(blob);
		downloadTag.download = fileName + ".txt";
		downloadTag.click();
		window.setTimeout( function() { URL.revokeObjectURL(blob);}, 10000 );
		window.alert('バックアップを保存しました。');
	} else {
		let downloadTag = document.createElement('a');
		downloadTag.addEventListener('click', async () => {
			const opts = {
				suggestedName: fileName,
				types: [{
					description: 'Text file',
					accept: {'text/plain': ['.txt']},
				}],
			};
			try{
				const handle = await window.showSaveFilePicker(opts);
				const writable = await handle.createWritable();
				await writable.write(save);
				await writable.close();
				window.alert('バックアップを保存しました。');
			} catch(e){
			}
		})
		downloadTag.click();
	}
}

上記コードでは
ファイル名はファイル名のテキストボックスを
用意するようにしていますが
ファイル名をこちらで指定してしまう事もできます。


セーブデータを圧縮して保存する場合

ブラウザでセーブデータを
圧縮して保存したい事は稀だとは思いますが、
セーブデータを圧縮して保存する場合は
以下のコードにすると良いでしょう。

圧縮してセーブデータを保存・読み込みする場合
async function save() {
	let fileName = document.getElementById('fileNameTextBox').value;
	const pattern = /[\\\/:\*\?\"<>\|]/;

	if(pattern.test(fileName)){
		window.alert('ファイル名に使用できない文字が含まれています。');
	}
	else {
		let save = 'SaveDataTypeA!ver1.0.0!';
		let saveOne = ""; //ここに保存する文字列を入れて下さい。

		const readableStream = new Response(saveOne).body.pipeThrough(new CompressionStream('deflate'));
		save += await bolbToBase64(await new Response(readableStream).blob());
		if(fileName == ''){
			var dt = new Date();
			var y = '' + dt.getFullYear();
			var m = ('00' + (dt.getMonth()+1)).slice(-2);
			var d = ('00' + dt.getDate()).slice(-2);
			var hour = ('00' + dt.getHours()).slice(-2);
			var minute = ('00' + dt.getMinutes()).slice(-2);
			var second = ('00' + dt.getSeconds()).slice(-2);
			fileName = "〇〇バックアップ" + (y + m + d + hour + minute + second); //〇〇にはサイト名を入れて下さい。
		}

		if(typeof window.showSaveFilePicker != 'function'){
			const blob = new Blob([save],{type:"text/plain"});
			let downloadTag = document.createElement('a');
			downloadTag.href = URL.createObjectURL(blob);
			downloadTag.download = fileName + ".txt";
			downloadTag.click();
			window.setTimeout( function() { URL.revokeObjectURL(blob);}, 10000 );
			window.alert('バックアップを保存しました。');
		}
		else{
			let downloadTag = document.createElement('a');
			downloadTag.addEventListener('click', async () => {
				const opts = {
					suggestedName: fileName,
					types: [{
						description: 'Text file',
						accept: {'text/plain': ['.txt']},
					}],
				};
				try{
					const handle = await window.showSaveFilePicker(opts);
					const writable = await handle.createWritable();
					await writable.write(save);
					await writable.close();
					window.alert('バックアップを保存しました。');
				} catch(e){
				}
			})
			downloadTag.click();
		}
	}
}
async function bolbToBase64(blob) {
	return await new Promise(resolve => {
		const reader = new FileReader();
		reader.readAsDataURL(blob);
		reader.onloadend = () => {
			resolve(reader.result.replace(/data:.*\/.*;base64,/, ''));
		};
	});
}

document.getElementById('files').addEventListener('change', function(e) {
	var result=e.target.files[0];
	var reader=new FileReader();
	reader.readAsText(result);
	reader.addEventListener('load', function() {
		myLoad(reader.result);
	});
	e.target.value = '';
});
async function myLoad(loadText) {
	let index = loadText.indexOf('!');
	if(!loadText.startsWith('SaveData')){
		window.alert('ファイルの読み込みに失敗しました。');
		return;
	}
	const isTypeA = loadText.startsWith('SaveDataTypeA');
	loadText = loadText.substr(index + 1);
	index = loadText.indexOf('!');
	const versionInfo = loadText.substr(0, index);

	loadText = loadText.substr(index + 1);
	const oneTimeBlob = await base64ToBlob(loadText);
	const readableStream = oneTimeBlob.stream().pipeThrough(new DecompressionStream('deflate'));
	loadText = await new Response(readableStream).text();
	
	//ここで loadText をロードしたい変数に代入して下さい。
}
async function base64ToBlob(base64) {
	return await fetch('data:application/octet-stream;base64,' + base64).then(res => res.blob());
}

参考サイト:
https://168iroha.net/blog/article/202401031600/

Compression Streams API を使用しています。
API の内部処理が変わってセーブデータが
正常に読み込めなくなった場合に
我々は責任を負わないものとさせて下さい。
何卒よろしくお願い致します。


以上 html で文字列をローカルに
保存するソースを公開しました。

この処理を書く時間を
短縮できたならば幸いです。

皆さんの開発の助けになれますように。
閲覧ありがとうございました。


追記

JavaScript の圧縮関連の
記事を書かれている方から
圧縮の箇所について提案頂きました。

用途ごとの最適な圧縮コードを頂けたため
是非コメント欄もご覧頂けると幸いです。

0
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0