UNIX 命令 file根據(jù)文件的內(nèi)容確認(rèn)一個文件類型,而不是使用文件擴(kuò)展名。例如,每個 GIF 文件都以字符 GIF開始,每個 JPEG 文件都以big-endian序的值 oxffd8 開始。
file命令要查詢一個 ASCII 文本文件,該文件一般位于 /etc/magic 中。你可以在一臺 UNIX 機(jī)器的 man 頁查看 magic 來瀏覽其格式。在 Apache Web 服務(wù)器上也有類似的文件。它一般用來確認(rèn)自己不知道的擴(kuò)展名的文件,然后返回正確的 MIME 類型。Apache 的版本很簡單,其處理范圍只包含一些很可能出現(xiàn)在 Web 服務(wù)器中的文件。
數(shù)據(jù)庫可以使用一個相似的技術(shù)來確定一個 BLOB 列的 MIME 類型。大多數(shù)據(jù)庫應(yīng)用程序在存儲數(shù)據(jù)的時候還會存儲某種類型的標(biāo)識。然而,可能對于某些人來說不能正確地標(biāo)識數(shù)據(jù)。因此,通過直接分析數(shù)據(jù)來檢查 BLOB 值的 magic 值然后返回一個 MIME 類型和解碼信息將會很有用。
將magic安裝到數(shù)據(jù)庫
安裝 magic 的過程使用一個 Perl 腳本產(chǎn)生一種可以使用 SQL*Loader 裝載到數(shù)據(jù)庫的標(biāo)準(zhǔn)化格式。數(shù)據(jù)然后由一個 PL/SQL 函數(shù)來掃描,該函數(shù)試圖確定 BLOB 的數(shù)據(jù)。下面是建立數(shù)據(jù)的步驟,以及一個例子:
第一步:創(chuàng)建一個 Perl 腳本將 magic 轉(zhuǎn)成一個 SQL*Loader 數(shù)據(jù)文件:
#!/usr/local/bin/perl
# --- magicdata.pl
# scan the "magic" file for file identification rules
$filename = ($#ARGV>=0) ? $ARGV[0] : 'magic';
open(FILE,"<$filename") die "Couldn't open file '$filename'";
$i = 0;
while (<FILE>)
{
next if /^/s*#/; # skip comments
next if /^/s*$/; # skip blank lines
s/[/r/n]*//g; # strip trailing cr/lf
# replace octal escape codes
s///([0-9]{3})/pack('C',oct($1))/eg;
# split on spaces, except for "/ "
my ($offset,$dt,$cnt,$mime,$encoding) = split(/(?<!//)/s+/);
$cont = ($offset =~ /^>/) ? 'Y' : undef;
$offset = substr($offset,1) if $cont;
if ($dteq 'string')
{
# generate a HEXTORAW version of the string
$data = join('',map(sPRintf('%02X',$_),unpack('C*',$cnt)));
}
else
{
# handle special number formats
if ($cnt =~ /^0x/) { $cnt = hex($cnt); } # hex
elsif ($cnt =~ /^0/) { $cnt = oct($cnt); } # octal
warn "unknown number: '$cnt'" unless $cnt =~ /^([0-9][1-9][0-9]*)$/;
if ($dteq 'belong') {
$data = sprintf('%02X' x 4,unpack('C4',pack('N',$cnt)));
} elsif ($dteq 'lelong') {
$data = sprintf('%02X' x 4,unpack('C4',pack('V',$cnt)));
} elsif ($dteq 'beshort' $dteq 'short') {
$data = sprintf('%02X' x 2,unpack('C2',pack('n',$cnt)));
} elsif ($dteq 'leshort') {
$data = sprintf('%02X' x 2,unpack('C2',pack('v',$cnt)));
} elsif ($dteq 'byte') {
$data = sprintf('%02X',$cnt);
} else {
warn "data type '$dt' not implemented";
}
}
$i++;
print join(',',$i,$cont,$offset,$data,$mime,$encoding),"/n";
}
close(FILE);
$ perl magicdata.pl $Oracle_HOME/Apache/conf/magic > magicdata.dat
第二步:在 SQL*Plus 中使用下面的 SQL 腳本創(chuàng)建表來保存這個數(shù)據(jù):
create table magicdata
(
line integer,
cont char(1),
offset integer,
data raw(24),
mime varchar2(24),
encoding varchar2(10)
);
第三步:使用 SQL*Loader 將前面產(chǎn)生的數(shù)據(jù)裝載到數(shù)據(jù)庫中:
load data
truncate
into table magicdata
fields terminated by ',' optionally enclosed by '"'
trailing nullcols
(
line,
cont,
offset,
data,
mime,
encoding
)
$ sqlldr user=scott/tiger control=magicdata.ctl data=magicdata.dat
下面是一些測試數(shù)據(jù),只有一個 BLOB 列的一個簡單的表。
drop table magictest;
create table magictest (myblob blob);
第四步:使用 SQL*Loader 從文件裝載三個圖像到這個表:
load data
infile *
into table magictest
fields terminated by ','
(
fname filler,
"MYBLOB" lobfile(fname) terminated by eof
)
begindata
file.bmp
file.gif
file.jpg
$sqlldruserid=scott/tiger control=magictest.ctl
下面是掃描 magic 數(shù)據(jù)表和 BLOB 以確定文件 MIME 類型的 PL/SQL 函數(shù):
create or replace function magic(lob_loc blob) return varchar2
is
continued boolean := false;
bdata raw(100);
begin
for rec in (select * from magicdata order by line) loop
if rec.cont = 'Y' then
if continued then
bdata := dbms_lob.substr
(
lob_loc,
utl_raw.length(rec.data),
rec.offset+1
);
if utl_raw.compare(bdata,rec.data) = 0 then
return rec.mime;
end if;
end if;
else
bdata := dbms_lob.substr
(
lob_loc,
utl_raw.length(rec.data),
rec.offset+1
);
dbms_output.put_line(bdata' <=> 'rec.data);
if utl_raw.compare(bdata,rec.data) = 0 then
if rec.mime is null then
continued := true;
else
return rec.mime;
end if;
end if;
end if;
end loop;
return null;
end magic;
/
show errors;
現(xiàn)在為了顯示它確實(shí)可以工作,我運(yùn)行下面的 SQL 語句:
Select magic(myblob) from testdata;
得到的輸出是:
MAGIC(MYBLOB)
-------------
image/bmp
image/gif
image/jpeg