ページ構成
タイトルとページが無駄に長くてすみません
各項目は、以下の通りです。
必要な部分のみご覧ください。
■概要
■参考サイト様
■注意事項
■本題1 WindowsでC#環境の場合のコード
■本題2 Android C#環境の場合のコード
■本題3 WindowsでVisual Basic環境の場合のコード
■動作テスト
★tarにアーカイブ後にGZipに圧縮する場合は
下記の記事も参考にしてください。
Visual Studio 2022のC#を使用し、Android12環境下で任意のファイルを直接tarファイルにバイナリを叩いて強引にアーカイブ後にtar.gzに圧縮する。コードのサンプルです。
https://qiita.com/oyk3865b/items/6f172ac280379ae4d696
概要
表題の通り
かつて私のブログにて公開したり
https://oyk3865b.blog.fc2.com/blog-entry-1609.html
動画として投稿した
https://www.nicovideo.jp/watch/sm23860323
任意のファイルを直接tarファイルにバイナリを叩いて
強引にアーカイブするコードのサンプルを、
今回、新たに自身への備忘録として
Visual Studio 2022のC#やVisual Basicにて、
Android12やWindows11環境下においても使用できるように作成しなおしたので
ここに記します。
尚、特段、NuGetなどで他のプラグインや
別途外部dllを必要としないようにしていますが、
その反面、とても強引なものなので、そのままのコード流用はご注意ください。
要は、下記の参考サイト様の情報を元に
tarのバイナリを内部構造のルールに従い、直接叩いています。
参考サイト様
◎『tar の構造 - インターネットの孤島』
http://www.redout.net/data/tar.html
◎『tar -- format of tar archives』
http://www.mkssoftware.com/docs/man4/tar.4.asp
その他コード中のURLのサイト様かより
コードの引用をしたり、参考にさせてもらった部分がございます。
この場に手厚く御礼申し上げます。
注意事項
※私は素人ですし
今回のコードは、あくまで最低限の動作する部分にとどめていますので、汚いです。
例外処理や、解放など至らぬ点が、多々ございます。ご了承ください。
もし実際に参考にされる際は、必要個所を必ず訂正・加筆してからにしてください。
※今回は特に内部でのファイル名の扱いや日時の扱いが
すごく適当です。
※動作は沢山メモリを使うので重いです。
その辺は度外視しています。
本題1
WindowsでC#環境の場合のコード
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace App1
{
class clsTarArchive
{
//出力用のバイナリ格納用
public List<byte> ary_tar_file;
public string filesize_str;
public async System.Threading.Tasks.Task Convert_File_to_TAR(
string Output_Path, string[] Archive_filepaths)
{ //指定したファイル達をTarにアーカイブするところ
//安全装置
//エラー個所の特定用
string err_string = "0";
if (Archive_filepaths.Length == 0) { return; }
try {
//■ファイルを作成して書き込む
using (System.IO.FileStream fs = new System.IO.FileStream(Output_Path,
System.IO.FileMode.Create,
System.IO.FileAccess.Write))
{
err_string = "1";
int content_count = Archive_filepaths.Length;
for (int i = 0; i < content_count; i += 1)
{ //各ファイルをループ
//出力TARバイナリの初期化
ary_tar_file = new System.Collections.Generic.List<byte> { };
ary_tar_file.Clear();
//一時格納用配列
System.Collections.Generic.List<byte> tar_write_binary = new System.Collections.Generic.List<byte> { };
tar_write_binary.Clear();
//■ヘッダー(512Bytes)の作成------------ここから-----------------
//http://www.redout.net/data/tar.html#ascii
//まずは、ファイル名を格納
//Uriからファイル名を取得
string filename = System.IO.Path.GetFileName(Archive_filepaths[i]); //await getRealPathFromURI(ac, Archive_filepaths[i]);
tar_write_binary.AddRange(System.Text.Encoding.UTF8.GetBytes(filename));
string v = "Open File : " + filename + "Size : " + filesize_str;
//lblsyori.Text = v;
err_string = "2";
retry_line:
if (tar_write_binary.Count >= 100)
{ //99文字以内にする
//→今回は、左から消して徐々に短くして対応する。
tar_write_binary.Clear();
filename = filename.Substring(filename.Length - 1);
tar_write_binary.AddRange(System.Text.Encoding.UTF8.GetBytes(filename));
goto retry_line;
}
//バイナリに書き込む予約
err_string = "2.5";
ary_tar_file.AddRange(tar_write_binary.ToArray());
tar_write_binary.Clear();
err_string = "3";
//→ファイル名ヘッダーを、100バイトであわせる
while (true) {
if (ary_tar_file.Count >= 100) { break; }
ary_tar_file.Add((byte)(0)); //0で埋める
}
v = "mode File : " + filename + "Size : " + filesize_str;
//lblsyori.Text = v;
//◆次に、mode→uid→gidの並びだが、面倒なので。
//固定値にする。解析が不十分orz
//◎mode
err_string = "4";
ary_tar_file.AddRange(System.Text.Encoding.ASCII.GetBytes(
"0100666"));
ary_tar_file.Add((byte)(0)); //0で区切る
//◎uid(所有者のユーザIDです。)
err_string = "5";
//http://www.c-lang.net/stat/index.html
ary_tar_file.AddRange(System.Text.Encoding.ASCII.GetBytes(
"0000002"));
ary_tar_file.Add((byte)(0)); //0で区切る
//◎gid(所有者のグループIDです。)
err_string = "6";
ary_tar_file.AddRange(System.Text.Encoding.ASCII.GetBytes(
"0000002"));
ary_tar_file.Add((byte)(0)); //0で区切る
//◆ファイルサイズを、8進数で示した文字列で埋め込む。
err_string = "7";
int filesize = 0;
var fi = new System.IO.FileInfo(Archive_filepaths[i]);
filesize_str = fi.Length.ToString();
if (int.TryParse(filesize_str, out filesize)) {
//10進数→8進数
filesize_str = Convert.ToString(filesize, 8);
//ファイルサイズ箇所は、11バイト分用意されている。
//それを超えるファイルは、扱えないので、そのファイルは飛ばす
if (filesize_str.Length > 11) { break; }
v = "8size File : " + filename + "Size : " + filesize_str;
//lblsyori.Text = v;
}
//空白桁は"0"で埋める
while (true)
{ //11バイトまで"0"文字で埋める
if (filesize_str.Length >= 11) { break; }
filesize_str = "0" + filesize_str;
}
ary_tar_file.AddRange(System.Text.Encoding.ASCII.GetBytes(filesize_str)); //バイナリに書き込む
tar_write_binary.Clear();
ary_tar_file.Add((byte)(0)); //0で区切る
filesize_str = "";
//◆アーカイブされた時点でのファイルの最終更新時刻
//★今回は手抜きで現在時間とする超強引コード
//(※※※こんなコードで、大丈夫か?※※※)
err_string = "8";
DateTime dtBirth3 = DateTime.Parse(DateTime.Now.Year.ToString() + "/" + DateTime.Now.Month.ToString() +
"/" + DateTime.Now.Day.ToString() + " " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString());
//秒を含まないシリアル値に変換する
long Last_W_Time = (long)(dtBirth3.ToBinary() / Math.Pow(10, 8));
//今回は、6213562917を、引いて、さらに10倍して
//擬似的に”stat() 関数で得られる最終更新時刻”の近似を得る。
Last_W_Time = (Last_W_Time - 6213562917) * 10;
//11桁の8進数に変換
filesize_str = Convert.ToString(Last_W_Time, 8);
//空白桁は"0"で埋める
while (true)
{ //11バイトまで"0"文字で埋める
if (filesize_str.Length >= 11) { break; }
filesize_str = "0" + filesize_str; //空白桁に"0"を付ける。
}
if (filesize_str.Length != 11) {
//変換に失敗したら、適当に、
filesize_str = "12211506030";// 2013/09/04 10:25とする。
}
////デバッグ用
//filesize_str = "12211506030";// 2013/09/04 10:25とする。
v = "date File : " + filename + "Date : " + filesize_str;
//lblsyori.Text = v;
tar_write_binary.AddRange(
System.Text.Encoding.ASCII.GetBytes(filesize_str)); //バイト配列に変換
ary_tar_file.AddRange(tar_write_binary.ToArray()); //バイナリに書き込む
tar_write_binary.Clear();
ary_tar_file.Add((byte)(0)); //0で区切る
//◆チェックサム→後で計算して入れるので、今は、8バイト適当に入れる。
int chksum_pos = ary_tar_file.Count; //開始位置を予め覚えておく
for (int k = 0; k < 8; k += 1) {
ary_tar_file.Add((byte)(0)); //ちなみに、0開始だと、147~154の位置
}
//◆typeflag/アーカイブの種類を決定する。
//とりあえず、今は、0(通常のファイル)にしておく
ary_tar_file.AddRange(System.Text.Encoding.ASCII.GetBytes("0"));
//◆linknameで100バイトの余白
for (int k = 0; k < 100; k += 1)
{
ary_tar_file.Add((byte)(0));
}
//◆magic フィールドと、version
v = "magic File : " + filename + "Date : " + filesize_str;
//lblsyori.Text = v;
ary_tar_file.AddRange(System.Text.Encoding.ASCII.GetBytes(
"ustar "));
ary_tar_file.Add((byte)(0)); //0で区切る
//◆uname 32バイト
tar_write_binary.AddRange(
System.Text.Encoding.ASCII.GetBytes(
"tagesp"));
while (true)
{ //32バイトまで"0"文字で埋める
if (tar_write_binary.Count >= 32) { break; }
tar_write_binary.Add((byte)(0)); //0で埋める
}
ary_tar_file.AddRange(tar_write_binary); //バイナリに書き込む
tar_write_binary.Clear();
//◆gname以降のヘッダー全て 215バイト
tar_write_binary.AddRange(
System.Text.Encoding.ASCII.GetBytes(
"tagesp"));
while (true)
{ //11バイトまで"0"文字で埋める
if (tar_write_binary.Count >= 215) { break; }
tar_write_binary.Add((byte)(0)); //0で埋める
}
ary_tar_file.AddRange(tar_write_binary.ToArray()); //バイナリに書き込む
tar_write_binary.Clear();
//◆◇◆ここが本番。チェックサムの格納。◆◇◆
Int32 chk_sum = 0;
int idx = 0;
while (true) {
if (idx >= ary_tar_file.Count) { break; }
if (idx >= 147 && idx <= 154) {
//チェックサム領域は、計算しない
} else {
//ヘッダーブロックの全バイトの和を 8 進数で表した値
chk_sum += Convert.ToInt32(ary_tar_file[idx]);
}
idx++;
}
//8進数に変換
//+256は、チェックサムの空白分
filesize_str = Convert.ToString(chk_sum + 256, 8);
//空白桁は"0"で埋める
while (true)
{ //7バイトまで"0"文字で埋める
if (filesize_str.Length >= 7) { break; }
filesize_str = "0" + filesize_str; //空白桁に"0"を付ける。
}
v = "chk_sum File : " + filename + "chk_sum : " + filesize_str;
//lblsyori.Text = v;
//チェックサムを、正しいものに書き換える。
tar_write_binary.AddRange(System.Text.Encoding.ASCII.GetBytes(filesize_str));
for (int k = 0; k < tar_write_binary.Count; k += 1)
{
int idx2 = k + chksum_pos;
ary_tar_file[idx2] = tar_write_binary[k];
}
tar_write_binary.Clear();
//await fs.WriteAsync((byte[])ary_tar_file.ToArray(), 0, ary_tar_file.Count);
//await fs.FlushAsync();
fs.Write((byte[])ary_tar_file.ToArray(), 0, ary_tar_file.Count);
//配列を、一旦、クリアする。
ary_tar_file.Clear();
tar_write_binary.Clear();
v = "write File : " + filename + "chk_sum : " + filesize_str;
//lblsyori.Text = v;
//■本体ファイルを書き込む-------ここから--------------------------
//http://dobon.net/vb/dotnet/file/filestream.html
//☆☆☆512の倍数でなければならない。☆☆☆
//ファイルを開く
byte[] buffer = new byte[512];
int len = 0; //今回読み込んだバイト数の格納
//https://stackoverflow.com/questions/2436385/android-getting-from-a-uri-to-an-inputstream-to-a-byte-array/2436413
//using (var inputStream = ac.ContentResolver.OpenInputStream(Archive_filepaths[i]))
using (System.IO.FileStream stream = new System.IO.FileStream(Archive_filepaths[i],
System.IO.FileMode.Open,
System.IO.FileAccess.Read))
{
//ファイルを開く
//http://d.hatena.ne.jp/curest/20090829/1251532479
while ((len = stream.Read(buffer, 0, buffer.Length)) != -1)
{ //ファイルを読み込む
if (len == 0) { break; } //終端で出る
//部分的に読み込んだデータをそのまま受け渡す
//await fs.WriteAsync(buffer, 0, buffer.Length);
//await fs.FlushAsync();
fs.Write(buffer, 0, buffer.Length);
//配列初期化
buffer = new byte[512];
}
//閉じる
stream.Close();
}
}
//■フッターの作成------------ここから-----------------
//1024バイトの空白。が、フッター
byte[] buffer2 = new byte[1024];
await fs.WriteAsync(buffer2, 0, buffer2.Length);
await fs.FlushAsync();
//■後処理
fs.Close(); //ファイルを閉じる
}
MessageBox.Show("Tarの処理が終わりました");
}
catch {
MessageBox.Show(err_string);
}
}
}
}
↓上記のコードの発動例
using App1;
using System;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void Form1_Load(object sender, EventArgs e)
{
using(OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Filter = "すべてのファイル(*.*)|*.*";
ofd.Title = "開くファイルを選択してください";
ofd.Multiselect = true;
//ダイアログを表示する
if (ofd.ShowDialog() == DialogResult.OK)
{
clsTarArchive clsT = new clsTarArchive();
//OKボタンがクリックされたとき、選択されたファイル名を表示する
await clsT.Convert_File_to_TAR(System.IO.Path.Combine(Environment.CurrentDirectory, "test.tar"),
ofd.FileNames);
}
}
Application.Exit();
}
}
}
本題2
Android C#環境の場合のコード
namespace App1
{
class clsTarArchive
{
//出力用のバイナリ格納用
public List<byte> ary_tar_file;
public string filesize_str;
public async System.Threading.Tasks.Task Convert_File_to_TAR(
Android.Net.Uri Output_Path, Android.Net.Uri[] Archive_filepaths,
Activity ac)
{ //指定したファイル達をTarにアーカイブするところ
//安全装置
string err_string = "0";
if (Archive_filepaths.Length == 0) { return; }
try {
//■ファイルを作成して書き込む
using (var fs = ac.ContentResolver.OpenOutputStream(Output_Path))
{
err_string = "1";
int content_count = Archive_filepaths.Length;
for (int i = 0; i < content_count; i += 1)
{ //各ファイルをループ
//出力TARバイナリの初期化
ary_tar_file = new System.Collections.Generic.List<byte> { };
ary_tar_file.Clear();
//一時格納用配列
System.Collections.Generic.List<byte> tar_write_binary = new System.Collections.Generic.List<byte> { };
tar_write_binary.Clear();
//■ヘッダー(512Bytes)の作成------------ここから-----------------
//http://www.redout.net/data/tar.html#ascii
//まずは、ファイル名を格納
//Uriからファイル名を取得
string filename = await getRealPathFromURI(ac, Archive_filepaths[i]);
tar_write_binary.AddRange(System.Text.Encoding.UTF8.GetBytes(filename));
//何かでの経過報告用文字列
string v = "Open File : " + filename + "Size : " + filesize_str;
err_string = "2";
retry_line:
if (tar_write_binary.Count >= 100)
{ //99文字以内にする
//→今回は、左から消して徐々に短くして対応する。
tar_write_binary.Clear();
filename = filename.Substring(filename.Length - 1);
tar_write_binary.AddRange(System.Text.Encoding.UTF8.GetBytes(filename));
goto retry_line;
}
//バイナリに書き込む予約
err_string = "2.5";
ary_tar_file.AddRange(tar_write_binary.ToArray());
tar_write_binary.Clear();
err_string = "3";
//→ファイル名ヘッダーを、100バイトであわせる
while (true) {
if (ary_tar_file.Count >= 100) { break; }
ary_tar_file.Add((byte)(0)); //0で埋める
}
v = "mode File : " + filename + "Size : " + filesize_str;
//◆次に、mode→uid→gidの並びだが、面倒なので。
//固定値にする。解析が不十分orz
//◎mode
err_string = "4";
ary_tar_file.AddRange(System.Text.Encoding.ASCII.GetBytes(
"0100666"));
ary_tar_file.Add((byte)(0)); //0で区切る
//◎uid(所有者のユーザIDです。)
err_string = "5";
//http://www.c-lang.net/stat/index.html
ary_tar_file.AddRange(System.Text.Encoding.ASCII.GetBytes(
"0000002"));
ary_tar_file.Add((byte)(0)); //0で区切る
//◎gid(所有者のグループIDです。)
err_string = "6";
ary_tar_file.AddRange(System.Text.Encoding.ASCII.GetBytes(
"0000002"));
ary_tar_file.Add((byte)(0)); //0で区切る
//◆ファイルサイズを、8進数で示した文字列で埋め込む。
err_string = "7";
int filesize = 0;
if(int.TryParse(filesize_str, out filesize)) {
//10進数→8進数
filesize_str = Convert.ToString(filesize, 8);
//ファイルサイズ箇所は、11バイト分用意されている。
//それを超えるファイルは、扱えないので、そのファイルは飛ばす
if (filesize_str.Length > 11) { break; }
v = "8size File : " + filename + "Size : " + filesize_str;
}
//空白桁は"0"で埋める
while (true)
{ //11バイトまで"0"文字で埋める
if (filesize_str.Length >= 11) { break; }
filesize_str = "0" + filesize_str;
}
ary_tar_file.AddRange(System.Text.Encoding.ASCII.GetBytes(filesize_str)); //バイナリに書き込む
tar_write_binary.Clear();
ary_tar_file.Add((byte)(0)); //0で区切る
filesize_str = "";
//◆アーカイブされた時点でのファイルの最終更新時刻
//★今回は手抜きで現在時間とする超強引コード
//(※※※こんなコードで、大丈夫か?※※※)
err_string = "8";
DateTime dtBirth3 = DateTime.Parse(DateTime.Now.Year.ToString() + "/" + DateTime.Now.Month.ToString() +
"/" + DateTime.Now.Day.ToString() + " " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString());
//秒を含まないシリアル値に変換する
long Last_W_Time = (long)(dtBirth3.ToBinary() / Math.Pow(10, 8));
//今回は、6213562917を、引いて、さらに10倍して
//擬似的に”stat() 関数で得られる最終更新時刻”の近似を得る。
Last_W_Time = (Last_W_Time - 6213562917) * 10;
//11桁の8進数に変換
filesize_str = Convert.ToString(Last_W_Time, 8);
//空白桁は"0"で埋める
while (true)
{ //11バイトまで"0"文字で埋める
if (filesize_str.Length >= 11) { break; }
filesize_str = "0" + filesize_str; //空白桁に"0"を付ける。
}
if (filesize_str.Length != 11) {
//変換に失敗したら、適当に、
filesize_str = "12211506030";// 2013/09/04 10:25とする。
}
v = "date File : " + filename + "Date : " + filesize_str;
tar_write_binary.AddRange(
System.Text.Encoding.ASCII.GetBytes(filesize_str)); //バイト配列に変換
ary_tar_file.AddRange(tar_write_binary.ToArray()); //バイナリに書き込む
tar_write_binary.Clear();
ary_tar_file.Add((byte)(0)); //0で区切る
//◆チェックサム→後で計算して入れるので、今は、8バイト適当に入れる。
int chksum_pos = ary_tar_file.Count; //開始位置を予め覚えておく
for (int k = 0; k < 8; k += 1) {
ary_tar_file.Add((byte)(0)); //ちなみに、0開始だと、147~154の位置
}
//◆typeflag/アーカイブの種類を決定する。
//とりあえず、今は、0(通常のファイル)にしておく
ary_tar_file.AddRange(System.Text.Encoding.ASCII.GetBytes("0"));
//◆linknameで100バイトの余白
for (int k = 0; k < 100; k += 1)
{
ary_tar_file.Add((byte)(0));
}
//◆magic フィールドと、version
v = "magic File : " + filename + "Date : " + filesize_str;
ary_tar_file.AddRange(System.Text.Encoding.ASCII.GetBytes(
"ustar "));
ary_tar_file.Add((byte)(0)); //0で区切る
//◆uname 32バイト
tar_write_binary.AddRange(
System.Text.Encoding.ASCII.GetBytes(
"tagesp"));
while (true)
{ //11バイトまで"0"文字で埋める
if (tar_write_binary.Count >= 32) { break; }
tar_write_binary.Add((byte)(0)); //0で埋める
}
ary_tar_file.AddRange(tar_write_binary); //バイナリに書き込む
tar_write_binary.Clear();
//◆gname以降のヘッダー全て 215バイト
tar_write_binary.AddRange(
System.Text.Encoding.ASCII.GetBytes(
"tagesp"));
while (true)
{ //11バイトまで"0"文字で埋める
if (tar_write_binary.Count >= 215) { break; }
tar_write_binary.Add((byte)(0)); //0で埋める
}
ary_tar_file.AddRange(tar_write_binary.ToArray()); //バイナリに書き込む
tar_write_binary.Clear();
//◆◇◆ここが本番。チェックサムの格納。◆◇◆
Int32 chk_sum = 0;
int idx = 0;
while (true) {
if (idx >= ary_tar_file.Count) { break; }
if (idx >= 147 && idx <= 154) {
//チェックサム領域は、計算しない
} else {
//ヘッダーブロックの全バイトの和を 8 進数で表した値
chk_sum += Convert.ToInt32(ary_tar_file[idx]);
}
idx++;
}
//8進数に変換
//+256は、チェックサムの空白分
filesize_str = Convert.ToString(chk_sum + 256, 8);
//空白桁は"0"で埋める
while (true)
{ //8バイトまで"0"文字で埋める
if (filesize_str.Length >= 8) { break; }
filesize_str = "0" + filesize_str; //空白桁に"0"を付ける。
}
v = "chk_sum File : " + filename + "chk_sum : " + filesize_str;
//チェックサムを、正しいものに書き換える。
tar_write_binary.AddRange(System.Text.Encoding.ASCII.GetBytes(filesize_str));
for (int k = 0; k < tar_write_binary.Count; k += 1)
{
int idx2 = k + chksum_pos;
ary_tar_file[idx2] = tar_write_binary[k];
}
tar_write_binary.Clear();
//await fs.WriteAsync((byte[])ary_tar_file.ToArray(), 0, ary_tar_file.Count);
//await fs.FlushAsync();
fs.Write((byte[])ary_tar_file.ToArray(), 0, ary_tar_file.Count);
//配列を、一旦、クリアする。
ary_tar_file.Clear();
tar_write_binary.Clear();
v = "write File : " + filename + "chk_sum : " + filesize_str;
//■本体ファイルを書き込む-------ここから--------------------------
//http://dobon.net/vb/dotnet/file/filestream.html
//☆☆☆512の倍数でなければならない。☆☆☆
//ファイルを開く
byte[] buffer = new byte[512];
int len = 0; //今回読み込んだバイト数の格納
//https://stackoverflow.com/questions/2436385/android-getting-from-a-uri-to-an-inputstream-to-a-byte-array/2436413
using (var inputStream = ac.ContentResolver.OpenInputStream(Archive_filepaths[i]))
{
using (Java.IO.BufferedInputStream stream = new Java.IO.BufferedInputStream(inputStream))
{
//ファイルを開く
//http://d.hatena.ne.jp/curest/20090829/1251532479
while ((len = stream.Read(buffer, 0, buffer.Length)) != -1)
{ //ファイルを読み込む
if (len == 0) { break; } //終端で出る
//部分的に読み込んだデータをそのまま受け渡す
//await fs.WriteAsync(buffer, 0, buffer.Length);
//await fs.FlushAsync();
fs.Write(buffer, 0, buffer.Length);
//配列初期化
Array.Fill<byte>(buffer, 0);
}
//閉じる
stream.Close();
}
inputStream.Close();
}
}
//■フッターの作成------------ここから-----------------
//1024バイトの空白。が、フッター
byte[] buffer2 = new byte[1024];
await fs.WriteAsync(buffer2, 0, buffer2.Length);
await fs.FlushAsync();
//■後処理
fs.Close(); //ファイルを閉じる
}
Android.Widget.Toast.MakeText(Android.App.Application.Context, "Tar処理が終わりました", Android.Widget.ToastLength.Long).Show();
}
catch {
//メッセージ表示(保存先表示)
Android.Widget.Toast.MakeText(Android.App.Application.Context, "※else_Err!※" + err_string + "※", Android.Widget.ToastLength.Long).Show();
}
}
public async System.Threading.Tasks.Task<string> getRealPathFromURI(Activity ac, Android.Net.Uri contentUri)
{ //Uriからファイル名を取得する。
//https://codeday.me/jp/qa/20181204/12998.html
Android.Database.ICursor cursor = null;
if (contentUri.Scheme.Equals("content"))
{ //コンテントタイプのUriの場合
try
{ //https://stackoverflow.com/questions/5568874/how-to-extract-the-file-name-from-uri-returned-from-intent-action-get-content
cursor = ac.ContentResolver.Query(contentUri, null, null, null, null);
if (cursor != null && cursor.MoveToFirst())
{
//ファイル名とサイズを取得しておく
string fff = cursor.GetString(cursor.GetColumnIndex(Android.Provider.OpenableColumns.DisplayName));
filesize_str = cursor.GetString(cursor.GetColumnIndex(Android.Provider.OpenableColumns.Size));
await System.Threading.Tasks.Task.Delay(30);
cursor.Close();
return fff;
}
}
finally
{
if (cursor != null)
{
cursor.Close();
}
}
}
else
{ //ファイルタイプのUriの場合
string result = contentUri.Path;
filesize_str = System.IO.File.ReadAllBytes(result).ToString();
result = result.Substring(result.LastIndexOf('/') + 1);
return result;
}
return "___";
}
}
}
↓上記のコードの発動例
namespace App1
{
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme.NoActionBar", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
clsTarArchive clsTar = new clsTarArchive();
public System.Collections.Generic.List<Android.Net.Uri> file_selected_uris;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
//Android11.0以上の場合のみ
string[] Manifest_Permissions = { Android.Manifest.Permission.WriteExternalStorage,
Android.Manifest.Permission.ReadExternalStorage,
Android.Manifest.Permission.AccessMediaLocation
};
//各権限をループ2
foreach (System.String Permission_str in Manifest_Permissions)
{
//https://docs.microsoft.com/ja-jp/xamarin/android/app-fundamentals/permissions?tabs=windows
//https://www.petitmonte.com/java/android_fileprovider.html
if (ApplicationContext.CheckCallingOrSelfPermission(Permission_str) !=
Android.Content.PM.Permission.Granted)
{ //許可されていない場合
// ストレージの権限の許可を求めるダイアログを表示する
//https://qiita.com/khara_nasuo486/items/f23c91ccd37db885aefe
if (AndroidX.Core.App.ActivityCompat.ShouldShowRequestPermissionRationale(this,
Permission_str))
{
AndroidX.Core.App.ActivityCompat.RequestPermissions(this,
Manifest_Permissions, (int)Android.Content.PM.RequestedPermission.Required);
}
else
{
Toast toast =
Toast.MakeText(ApplicationContext, "アプリ実行の権限が必要です", ToastLength.Long);
toast.Show();
AndroidX.Core.App.ActivityCompat.RequestPermissions(this,
Manifest_Permissions,
(int)Android.Content.PM.RequestedPermission.Required);
}
}
}
Button btn_main = FindViewById<Button>(Resource.Id.btn_main);
btn_main.Click += delegate
{
Intent imageIntent = new Intent(Intent.ActionGetContent);
imageIntent.SetType("*/*");
//複数画像選択かどうか?0なら複数選択
//https://stackoverflow.com/questions/19585815/select-multiple-images-from-android-gallery
//PDFとzipは複数。
imageIntent.PutExtra(Intent.ExtraAllowMultiple, true);
imageIntent.SetAction(Intent.ActionGetContent);
StartActivityForResult(
Intent.CreateChooser(imageIntent, "アーカイブするファイル選択してください"), 0);
};
}
protected override async void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
string err_string = "500";
try
{
//◆◆◆以下、画像選択時のイベント◆◆◆
if (requestCode == 0) {
if (resultCode == Result.Ok)
{
file_selected_uris = new System.Collections.Generic.List<Android.Net.Uri>();
file_selected_uris.Clear();
err_string = "501";
if (data.ClipData != null)
{ //複数選択された場合
int count = data.ClipData.ItemCount;
int currentItem = 0;
while (currentItem < count)
{ //各ファイルのURIを取得
currentItem = currentItem + 1;
file_selected_uris.Add(data.ClipData.GetItemAt(currentItem - 1).Uri);
}
}
else if (data.Data != null)
{ //1つだけの選択時
file_selected_uris.Add(data.Data);
}
err_string = "502";
//Windowsでいう所の「名前を付けて保存」ダイアログの作成
Intent intent = new Intent(Intent.ActionCreateDocument);
intent.AddCategory(Intent.CategoryOpenable);
intent.SetType("application/x-tar");
//ファイル名は提案するだけで保存先などは最終的にこのダイアログでユーザーに決められる
intent.PutExtra(Intent.ExtraTitle, System.IO.Path.GetFileName("test.tar"));
err_string = "503";
//今回はダイアログが終わると200を足した値で帰ってくる
StartActivityForResult(Intent.CreateChooser(intent, "保存先の指定"), 200);
return;
}
}
else if (requestCode == 200)
{
if (resultCode == Result.Ok)
{ //OK(ズドン)
err_string = "504" + (data.Data == null) ;
await clsTar.Convert_File_to_TAR(
data.Data, file_selected_uris.ToArray(), this);
}
}
}
catch {
//メッセージ表示(保存先表示)
Android.Widget.Toast.MakeText(Android.App.Application.Context, "※else_Err!※" + err_string + "※", Android.Widget.ToastLength.Long).Show();
}
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
return true;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
int id = item.ItemId;
return base.OnOptionsItemSelected(item);
}
}
}
本題3
Windows Visual Baisc環境の場合のコード
Public Class Form1
'出力用のバイナリ格納用。
Private ary_tar_file As New List(Of Byte)
'出力するTARファイルの出力先フォルダを格納
'★★デスクトップに出力固定。★★
Private Output_tar_Directory As String = System.Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)
'出力するTARファイルのフルパスを格納
Private Output_tar_Path As String
'有効なファイル数を格納するリスト。
Private Convert_ArchiveFile_List As New List(Of String)
Private Function Convert_File_to_TAR() As Integer
'★★★★TARを、実際に作っていくルーチン★★★
'https://www.mkssoftware.com/docs/man4/tar.4.asp
'安全装置
If Convert_ArchiveFile_List.Count <= 0 Then '指定・ファイルがない場合
Return -99 '失敗を返す
End If
'■ファイルを作成して書き込む
'http://dobon.net/vb/dotnet/file/filestream.html
'ファイルが存在しているときは、上書きする
'Create a Initialization TAR file.
Dim fs As New System.IO.FileStream(Output_tar_Path,
System.IO.FileMode.Create,
System.IO.FileAccess.Write)
'処理結果を格納
Dim Convert_File_to_TAR_ret As Integer = 0 '初期化
For Each FileName As String In Convert_ArchiveFile_List
'各ファイルについて、ループする。
'出力TARバイナリの初期化 / Initialization
ary_tar_file = New List(Of Byte)
'■ヘッダー(512Bytes)の作成------------ここから-----------------
'http://www.redout.net/data/tar.html#ascii
Dim tar_write_binary() As Byte = Nothing
Dim Input_FileName As String = IO.Path.GetFileName(FileName)
''ディレクトリ構造・テスト用
'Input_FileName = "grp/" & Input_FileName 'grpというフォルダの中に作成される。
Retry_Input_FileName_Line:
'◆まずは、ファイル名を格納
'※ここだけ、SHIFT-JISコード扱いなので注意。
If System.Text.Encoding.UTF8.GetByteCount(
Input_FileName) <= 99 Then
'99バイト文字以内の場合
tar_write_binary = System.Text.Encoding.UTF8.GetBytes(
Input_FileName)
ary_tar_file.AddRange(tar_write_binary) 'バイナリに書き込む
Erase tar_write_binary
Else '99バイト文字を超えた場合→今回は、徐々に短くして対応する。
Dim fff As String = IO.Path.GetFileNameWithoutExtension(Input_FileName)
fff = fff.Substring(0, fff.Length - 2) & "~"
Input_FileName = fff & IO.Path.GetExtension(FileName)
GoTo Retry_Input_FileName_Line 'やり直し
End If
'→ファイル名ヘッダーを、100バイトであわせる
For i = ary_tar_file.Count To 99
ary_tar_file.Add(CByte(0)) '0で埋める
Next
'◆次に、mode→uid→gidの並びだが、面倒なので。
'固定値にする。解析が不十分orz
'◎mode
tar_write_binary =
System.Text.Encoding.ASCII.GetBytes(
"0100666")
ary_tar_file.AddRange(tar_write_binary) 'バイナリに書き込む
Erase tar_write_binary
ary_tar_file.Add(CByte(0)) '0で区切る
'◎uid(所有者のユーザIDです。)
'http://www.c-lang.net/stat/index.html
tar_write_binary =
System.Text.Encoding.ASCII.GetBytes(
"0000002")
ary_tar_file.AddRange(tar_write_binary) 'バイナリに書き込む
Erase tar_write_binary
ary_tar_file.Add(CByte(0)) '0で区切る
'◎gid(所有者のグループIDです。)
tar_write_binary =
System.Text.Encoding.ASCII.GetBytes(
"0000002")
ary_tar_file.AddRange(tar_write_binary) 'バイナリに書き込む
Erase tar_write_binary
ary_tar_file.Add(CByte(0)) '0で区切る
'◆ファイルサイズを、8進数で示した文字列で埋め込む。
Dim fi As New System.IO.FileInfo(FileName)
'8進数の文字列を取得
Dim LenB_Str As String = Convert.ToString(fi.Length, 8)
'ファイルサイズ箇所は、11バイト分用意されている。
tar_write_binary = System.Text.Encoding.ASCII.GetBytes(LenB_Str)
'それを超えるファイルは、扱えないので、飛ばす
If tar_write_binary.Length > 11 Then Continue For
For i = tar_write_binary.Length To 10
'空白桁は"0"で埋める
ary_tar_file.AddRange(System.Text.Encoding.ASCII.GetBytes("0"))
Next
ary_tar_file.AddRange(tar_write_binary) 'バイナリに書き込む
Erase tar_write_binary
ary_tar_file.Add(CByte(0)) '0で区切る
'◆アーカイブされた時点でのファイルの最終更新時刻
'→今は、とりあえず、適当でええやん。ということで、超強引なコードです。
LenB_Str = "" '文字列初期化
Try 'ファイル最終更新時刻のシリアル値取得
'http://www.answers.com/topic/getfiletime
Dim dtBirth3 As DateTime = System.IO.File.GetLastWriteTime(FileName)
'後の処理のため、秒のデータを消し、取得し直す(秒を含まないシリアル値に直接変換する方法わ知らないorz)
dtBirth3 = DateTime.Parse(dtBirth3.Year.ToString & "/" & dtBirth3.Month.ToString & "/" & dtBirth3.Day.ToString & " " & dtBirth3.Hour.ToString & ":" & dtBirth3.Minute.ToString)
'秒を含まないシリアル値に変換する
Dim Last_W_Time As Long = (dtBirth3.ToBinary() \ (10 ^ 8))
'今回は、6213562917を、引いて、さらに10倍して
'擬似的に”stat() 関数で得られる最終更新時刻”の近似を得る。(※※※こんなコードで、大丈夫か?※※※)
Last_W_Time = (Last_W_Time - 6213562917) * 10
'11桁の8進数に変換
LenB_Str = Convert.ToString(Last_W_Time, 8)
Do While LenB_Str.Length < 11
LenB_Str = "0" & LenB_Str '空白桁に"0"を付ける。
Loop
Catch ex As Exception
'エラー時→そのまま進む
'ちなみに、ここの更新日時を、"99999999999"や、"00000000000"にしたら、更新日時が空白のファイルになった。
End Try
If LenB_Str.Length <> 11 Then
'変換に失敗したら、適当に、
LenB_Str = "12211506030" '2013/09/04 10:25とする。
End If
''デバッグ用
'LenB_Str = "12211506030"
tar_write_binary =
System.Text.Encoding.ASCII.GetBytes(LenB_Str) 'バイト配列に変換
ary_tar_file.AddRange(tar_write_binary) 'バイナリに書き込む
Erase tar_write_binary
ary_tar_file.Add(CByte(0)) '0で区切る
'◆チェックサム→後で計算して入れるので、今は、8バイト適当に入れる。
Dim chksum_pos As Long = ary_tar_file.Count '開始位置を予め覚えておく
For i As Integer = 0 To 7
ary_tar_file.Add(CByte(0)) 'ちなみに、0開始だと、147~154の位置
Next
'◆typeflag/アーカイブの種類を決定する。
'とりあえず、今は、0(通常のファイル)にしておく
tar_write_binary = System.Text.Encoding.ASCII.GetBytes("0")
ary_tar_file.AddRange(tar_write_binary) 'バイナリに書き込む
Erase tar_write_binary
'◆linknameで100バイトの余白
For i As Integer = 0 To 99
ary_tar_file.Add(CByte(0))
Next
'◆magic フィールドと、version
tar_write_binary =
System.Text.Encoding.ASCII.GetBytes(
"ustar ")
ary_tar_file.AddRange(tar_write_binary) 'バイナリに書き込む
Erase tar_write_binary
ary_tar_file.Add(CByte(0)) '0で区切る
'◆uname 32バイト
tar_write_binary =
System.Text.Encoding.ASCII.GetBytes(
"tagesp")
ary_tar_file.AddRange(tar_write_binary) 'バイナリに書き込む
Erase tar_write_binary
For i As Integer = 0 To 25
ary_tar_file.Add(CByte(0))
Next
'◆gname以降のヘッダー全て
tar_write_binary =
System.Text.Encoding.ASCII.GetBytes(
"tagesp")
ary_tar_file.AddRange(tar_write_binary) 'バイナリに書き込む
Erase tar_write_binary
For i As Integer = 0 To 208
ary_tar_file.Add(CByte(0)) '0で埋める。
Next
'◆◇◆ここが本番。チェックサムの格納。◆◇◆
Dim chk_sum As Int32 = 0
For i As Int32 = 0 To ary_tar_file.Count - 1
If i >= 147 AndAlso i <= 154 Then
'チェックサム領域は、計算しない
Else 'ヘッダーブロックの全バイトの和を 8 進数で表した値
chk_sum += Convert.ToInt32(ary_tar_file(i))
End If
Next
'8進数での表示
'http://dobon.net/vb/dotnet/programing/converthex.html
LenB_Str = Convert.ToString(chk_sum + 256, 8) '+256は、チェックサムの空白分
Do While LenB_Str.Length < 7
LenB_Str = "0" & LenB_Str '空白桁に"0"を付ける。
Loop
'チェックサムを、一文字ずつ指定位置に書き換える。
For i = chksum_pos To chksum_pos + 6
Dim lenB_fff As String = LenB_Str.Substring(i - chksum_pos, 1)
tar_write_binary = System.Text.Encoding.ASCII.GetBytes(lenB_fff)
ary_tar_file(i) = CByte(tar_write_binary(0))
Next
Try
'一旦、バイト型配列の内容をすべて上書き
'Writing byte binary to new TAR file.
fs.Write(DirectCast(ary_tar_file.ToArray(), Byte()), 0, ary_tar_file.Count)
Catch ex As Exception
'書き込みエラー時
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Convert_File_to_TAR_ret = -1 '失敗フラグ
GoTo Exit_Line
End Try
'配列を、一旦、クリアする。
ary_tar_file.Clear() : Erase tar_write_binary
'■ヘッダーの作成------------ここまで-----------------
'■本体ファイルを書き込む-------ここから--------------------------
'http://dobon.net/vb/dotnet/file/filestream.html
'☆☆☆512の倍数でなければならない。☆☆☆
'ファイルを開く
Using fs_Input As New System.IO.FileStream(FileName,
System.IO.FileMode.Open,
System.IO.FileAccess.Read)
'ファイルを一時的に読み込む512バイト型配列を作成する
tar_write_binary = New Byte(511) {}
'ファイルをすべて読み込む
While True
'ファイルの一部を512バイトずつ読み込む
Dim readSize As Integer = fs_Input.Read(tar_write_binary, 0, tar_write_binary.Length)
'ファイルをすべて読み込んだときは終了する
If readSize = 0 Then
Exit While
End If
'部分的に読み込んだデータをそのまま受け渡す
fs.Write(tar_write_binary, 0, tar_write_binary.Length)
'配列の初期化
Array.Fill(Of Byte)(tar_write_binary, 0)
End While
'閉じる
fs_Input.Close()
End Using
'配列を、一旦、クリアする。
Erase tar_write_binary
'■本体ファイルを書き込む-------ここまで--------------------------
'成功カウント
Convert_File_to_TAR_ret += 1
Next
'■フッターの作成------------ここから-----------------
For i = 0 To 1023 '1024バイトの空白。が、フッター
ary_tar_file.Add(CByte(0))
Next
Try
'一旦、バイト型配列の内容をすべて上書き
'Writing byte binary to new TAR file.
fs.Write(DirectCast(ary_tar_file.ToArray(), Byte()), 0, ary_tar_file.Count)
Catch ex As Exception
'書き込みエラー時
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Convert_File_to_TAR_ret = -1 '失敗フラグ
GoTo Exit_Line
End Try
'配列を、クリアする。
ary_tar_file.Clear()
Exit_Line:
'■後処理
fs.Close() 'ファイルを閉じる
fs = Nothing
'バイナリ・メモリの占有を、解放する。
ary_tar_file.Clear()
ary_tar_file = Nothing
'各・解放
For Each img As Object In Convert_ArchiveFile_List
img = Nothing
Next
'初期化
Convert_ArchiveFile_List = Nothing
Return Convert_File_to_TAR_ret
End Function
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'ココカラハジマル・・・・・・・・・・・・・
Try '■TARに結合するファイルを選択する。
Using OpenFileDialog1 As New OpenFileDialog
OpenFileDialog1.Filter = "全ファイル|*.*"
OpenFileDialog1.Title = "tarに変換するファイルを選択してください"
OpenFileDialog1.Multiselect = True
'ダイアログを表示する
If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
'OKボタンがクリックされたとき
'■出力先の設定2
Output_tar_Path = IO.Path.Combine(Output_tar_Directory, IO.Path.GetFileNameWithoutExtension(OpenFileDialog1.FileNames(0)) & ".tar")
Convert_ArchiveFile_List.Clear()
'変換するファイルのリストを受け取る
Convert_ArchiveFile_List.AddRange(OpenFileDialog1.FileNames)
Else 'キャンセル時
GoTo Close_Line
End If
End Using
Catch ex As Exception
'何らかのエラー時
MessageBox.Show("大きなエラー:" & Environment.NewLine _
& ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
GoTo Close_Line
End Try
'TARを、作成していく。
If Convert_File_to_TAR() >= 0 Then
'続いて、tar.gzにするかどうか尋ねる。
MessageBox.Show("tarが、できました。")
End If
Close_Line:
'閉じる
Me.Close()
End Sub
Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
Me.Dispose()
Me.DestroyHandle()
End Sub
End Class
動作テスト
以上の方法で、作成したtarは、
・TAR ファイルをオンラインで無料で開きます。迅速と安全! - ezyZip
以上のサービスでは、解凍できましたが。
ソフト・サービスによっては、
「破損ファイル」として、扱われる可能性もございます。
何卒、ご了承いただきますように、
お願い申し上げます。