質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

Q&A

解決済

2回答

5210閲覧

perlでpdfファイルをアップする機能を追加したい。

terataka

総合スコア61

Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

0グッド

0クリップ

投稿2016/08/16 06:03

用語が微妙におかしかったりすると思うのでご容赦ください。

perlプログラムにpdfファイルをアップする機能をサブルーチンとして追加させたいです。
アップする部分に関してはこちらのプログラムを参考にしようとしています。
http://www.tohoho-web.com/cgi/wwwupl2.txt

したいことを箇条書きにすると…

・アップするのは自分だけ・他にもいろいろな情報をアップしますが、この項目ではpdfファイルだけをアップ。
($append_suffix機能の排除・ファイル拡張子の固定等)

・ファイル名を記事Noで保存したい。
80行目くらいからファイル名を取得しているように思うのですが、この部分をすべてキャンセルして、指定のファイル名で保存できるようにしたい。
ファイル名はこのサブルーチンに入る前に作成する記事NO($no)を利用。
これについては、「# ボディ処理」の上でファイル名の指定を割り込ませたらそのファイル名でアップできていたので問題なくできるのではないかと思ってます。

#ここにファイル名割り込み
$filename = "";
$filename = "$fn-2.pdf";
($fnはテスト用に設定しただけです。組み込み先では$noを取得)

・「INPUT TYPE=file name="up2"」にファイル名が入力された時だけ動作させる
これについては、
if ($in{'up2'}) { &up2; }
でさせるのは良いんですけど、参考プログラムのwwwupl2をそのままサブルーチンで動作させると、送られる変数全部を繰り返しアップするような感じになりますよね。
(すみません表現がうまくできないです。
テストでwwwupl2を動作させたとき、ファイル名を固定させようとして「<input type="hidden" name="fn" value="01-2">」としてファイルを送信したのですが、送信結果にhiddenの分も掲載されてました。今回の物を組み込んだ際、題名や内容・日付・pdfファイルのサムネイル画像用jpgファイル等も同時に保存予定です。このままだとそれも全部繰り返しちゃいますよね?
up2にファイルが指定された時、この部分に関してのみアップロード作業を行う(?)という風にしたいです。)

用語もつたない状態ですが、これで何とか伝わるでしょうか?
よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

あくまで、参考にどうぞ。
一切質問の内容を考慮しておりません。

アップロードファイルを指定するフォーム

up.htmlとでも保存

lang

1<html> 2<head> 3 <meta charset="utf-8"> 4</head> 5<body> 6 7 <form enctype="multipart/form-data" method="post" action="up.cgi"> 8 <table border="1" cellspacing="2" bordercolor="#004080" cellpadding="3"> 9 <tr> 10 <td valign="top" align="center">アップロードするファイルの選択</td> 11 </tr> 12 <tr> 13 <td align="center"><input type="file" name="file"></td> 14 </tr> 15 <tr> 16 <td valign="top" align="center"><input value="アップロード" type="submit" name="submit"></td> 17 </tr> 18 </table> 19 <input type="hidden" value="0" name="edit"> 20 </form> 21 22</body> 23</html> 24

ポイントは<form>タグに、enctype="multipart/form-data"を指定すること。
ほか、アップしたい情報があるのであれば、<form>~</form>の間に追記していく。

ファイルを受け取るCGI/perl

up.cgiというファイル名で保存。
ファイル名を変更したい場合は、上記HTML<form>タグ内の、action="up.cgi"も合わせて変更すること。

lang

1#!/usr/local/bin/perl 2 3#書き出し宣言 4print "Content-type: text/html\n\n"; 5print '<meta charset="utf8">'; 6 7 8#使用するモジュールをロード 9use File::Basename; 10use File::Copy; 11use CGI; 12 13#転送できるファイルの最大サイズを設定 14#(実際は、post送信されるコンテンツ合計の最大サイズ) 15#この値は、CGIオブジェクトを作成する時には既に 16#設定されていなければならない 17$CGI::POST_MAX = 1024 * 1000; #max = 1MB 18 19 20#CGIオブジェクトを作成 21$form = new CGI; 22 23 24#クライアントにヘッダを送信 25#これは、結果メッセージ表示のため 26binmode STDOUT; 27 28 29#ファイルの転送のチェック 30if (!defined($filename) and $error = $form->cgi_error){ 31 print "ファイルが転送できませんでした:$error\n<br>"; 32 print 'ファイルのサイズが大きすぎます。<br>'; 33 print '1MBまでにしてください。<br>'; 34 exit; 35} 36 37 38#ファイルハンドル作成 39my $fh = $form->upload('file'); 40 41#ファイルの存在の確認 42my $fname = basename($form->param('file')); 43if ($fname eq "./"){ 44 print 'ファイルのアップロードに失敗しました<br>'; 45 exit; 46} 47 48#ファイルの保存 49my $newfile = $fname; 50 51#unlink("$newfile"); 52copy ($fh, "$newfile.txt"); 53undef $form; 54 55#権限変更 56#chmod 0600,"$newfile"; 57 58print "ファイル名「$newfile」で書き出し"; 59 60exit; 61

これで、ファイルのアップロードができます。

投稿2016/09/16 00:34

nnahito

総合スコア2004

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

とほほさんのページは、さすがに情報が古い(12年前!)ので、
もう少し新しい情報を参考にしたほうが良いと思います。

今はこのような処理をするのはモジュールを使って行うのが一般的です。
下記の記事はモジュールを使って行う方法ですが、
これはそんなに新しい方法と言うわけでもありません。

ファイルアップロードの仕方
http://www.hidekik.com/cookbook/p2h.cgi?id=upload

・ファイル名を記事Noで保存したい。

とほほさんの方法でもモジュールを使う方法でも、
任意のファイル名を付けることは特に難しくありません。

送信されてきたデータはまだファイルになっていないので、
そのデータを取り出して任意のファイル名で保存すれば良いだけです。

・アップするのは自分だけ・他にもいろいろな情報をアップしますが、この項目ではpdfファイルだけをアップ。
・「INPUT TYPE=file name="up2"」にファイル名が入力された時だけ動作させる

フォームから送信した時点でデータは全てサーバーに送信されてしまうので、
サーバー側(PerlCGI側)で特定のデータだけアップロードしない、という操作はできません。

送信した時に、特定のデータだけ送るようにしたいとしたら、
それはPerl側ではなく、おそらくHTML+JavaScriptの範疇です。
JavaScriptなどのタグで別の質問にしたほうが良いと思います。

投稿2016/08/18 13:15

argius

総合スコア9388

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

terataka

2016/08/22 00:39

返答ありがとうございます。 早速組み込んでみました。 しかし、何をやっても「500 nternal Server Error」が出るのでおかしいなと思い、組み込まず参考サイトの通りに2ファイル(upload.htmlとupload.cgi)を作ってアップして動作確認させてみました。 結果は同じく、「500 nternal Server Error」が出ます。 ペースとなるCGIに組み込んで試行錯誤して分かった事は use CGI; use File::Copy; use File::Basename; my $q = new CGI;   &error("ここまで来てる?"); ←ここにエラーを入れるとエラーページを表示する。 my $fname = basename($q->param('upfile'));   &error("ここまで来てる?"); ←ここにエラーを入れると500エラーを表示する。 という感じです。 また、ファイル名をナンバリングにしたかったので、 my $fname = $no.pdf としてみると、エラーは出ないのですが(他の情報は正常にアップされ、完了画面を表示する)、pdfファイルはアップされません。 もしかしてですけど、私の使用しているサーバで「use CGI」「use File::Copy」「use File::Basename」等が使用できないという事でしょうか? また、pdfがダメなのかとも思ったのですが、軽いjpgファイルでもアップできませんでした。 何か解決策があるでしょうか? よろしくお願いします。
argius

2016/08/22 10:20

モジュールが無くてエラーになる場合は、useのところでエラーになるはずなので、 モジュール自体は使えるんだと思います。 レンタルサーバーのエラーログを見るか、 そうでなければ、use CGI::Carp qw(fatalsToBrowser);を使ってエラーを特定してみて下さい。 CGI::Carpを使う場合は、デバッグが終了したら安全のため外して下さい。 CGI::Carpについては、下記ページの"Perlエラーをブラウザ・ウィンドウに表示させる"という項目に説明があります。 CGI::Carp - HTTPD(またはその他)にエラーログを書込むためのCGIルーチン - perldoc.jp http://perldoc.jp/docs/modules/CGI-2.89/CGI/Carp.pod
terataka

2016/08/23 04:11

返答ありがとうございます。 まずは、参考サイトの通りに2ファイル(upload.htmlとupload.cgi)での動作確認ですが、こちらは私の記述ミスで動作していなかっただけでした。 組み込んだ場合のcgiを以下のようにしてみました。 use CGI; use File::Copy; use File::Basename; my $q = new CGI; use CGI::Carp qw(fatalsToBrowser warningsToBrowser); use CGI qw(:standard); print header(); warningsToBrowser(1); my $pfname = basename($q->param('pdffile')); my $ppath = ".img"; my $newfile = "$ppath/$pfname"; my $fh = $q->upload('pdffile'); copy ($fh, "$newfile"); undef $q; エラーは Software error: fileparse(): need a valid pathname at list.cgi line 399 と出ます。 line 399は、 my $pfname = basename($q->param('pdffile'));の部分です。 エラーは出てますけど、ファイル名はナンバーを利用する予定だったので、 my $pfname = $no.pdf; と変えてみると、前回と変わらずエラーも出ずファイルもアップされずという状況です。
argius

2016/08/23 13:03

少なくともcopy()のところまでが正常に処理されないと、ファイルのアップロードは完結しないと思います。 順番に解決していきましょう。 そのエラーが出るとき、$q->param('pdffile')には値が入っていますか? 何が入っていますか? それと、エラーには関係ないですが、 useはまとめて書いた方が良いと思います。 my $q = new CGI; は warningsToBrowser(1); の後ろに移動してください。 CGI::Carpを後で消すから不要な作業と思われるかも知れませんが、そうする習慣をつけておいた方が無難です。 use~warningsToBrowser(1);までが準備で、 処理自体の開始はmy $q = new CGI;というイメージです。
terataka

2016/08/24 00:18

返答ありがとうございます。 >そのエラーが出るとき、$q->param('pdffile')には値が入っていますか? これはどのように確認ししたらよいでしょうか? 普段変数がちゃんと送られているかを確認する時は、 &error("ここまではデータが来てる■$in{'aaa'}■"); などと記述してエラー画面で確認してます。 という事で、 ~ use File::Basename; my $q = new CGI; &error("入ってる?■$q->param('pdffile')■"); の位置に置いて実行すると、 入ってる?■CGI=HASH(0x1ba4540)->param('pdffile')■ となります。 ・・・これじゃダメですよね? use CGI::Carp の位置ですが、↑の&errorと同じ利用方法で、"エラーが出る場所に入れないとダメ"と思ってました。 その為、「my $q = new CGI;まではちゃんと動いてるみたいだから、その次に入れてみよう」としてました。 位置移動しておきます。
argius

2016/08/24 00:33

> &error("入ってる?■$q->param('pdffile')■"); > の位置に置いて実行すると、 $q->param('pdffile')という形式は、文字列中に書くと上手く行かないので、 &error("入ってる?■" . $q->param('pdffile') . "■"); としてみて下さい。 .はPerlの文字列連結演算子です。
terataka

2016/08/24 00:51

返答ありがとうございます。 >&error("入ってる?■" . $q->param('pdffile') . "■"); 入ってる?■■ と表示されました。何も入って無いって事ですよね…
terataka

2016/08/24 01:04

もう1つ情報として… ログを書きこむサブルーチン内に、 # ファイル添付処理 if ($in{'upfile1'}) { &UpFile1; } #pdfのサムネイルアップ用です。 if ($in{'pdffile'}) { &pdffileup; } として、上述のコードは sub pdffileup { にあります。 pdffileupのサブルーチンには届いているので、フォームの「name = pdffile」が空だったり綴りが間違っているという事はないと思います。
argius

2016/08/24 01:42

いったん、basename($q->param('pdffile'));のところは後回しにしましょう。 アップロードするファイルがtest.pdfだとして、 ファイル名を固定にしてみて、アップロードできるか確認してみて下さい。 # my $pfname = basename($q->param('pdffile')); my $pfname = "test.pdf"; > ログを書きこむサブルーチン内に、 これの$inというのは何ですか? 質問でもいきなり出てきているので何が入っているのか分かりません。
terataka

2016/08/24 04:32

返答ありがとうございます。 ># my $pfname = basename($q->param('pdffile')); >my $pfname = "test.pdf"; エラーは表示されず、その他情報(↓にフォームの内容の一部を記載てしいますが、内容や日付等)はログに書き込まれていますが、ファイルはアップされません。 >これの$inというのは何ですか? 説明が下手で申し訳ないです。 フォームの画面で print <<"EOM"; <form action="$script" method="POST" enctype="multipart/form-data"> <input type="hidden" name="mode" value="regist"> ~ <tr> <th scope="row">内容</td> <td> <textarea name="comment" style="width: 400px;height: 200px;">$comment</textarea> </td> </tr> <tr> <th scope="row">画像</td> <td><input type="file" name="upfile1" style="width: 400px;"><br /</td> </tr> <tr> <th scope="row">PDFファイル</td> <td> <input type="file" name="pdffile" style="width: 400px;"> </td> </tr> ~ </form> という風にして、 #----------------# # デコード処理 # #----------------# sub decode { &ReadParse; while (($key,$val) = each %in) { if ($key ne "upfile1") { # シフトJISコードに変換 #&jcode'convert(*val, "sjis", "", "z"); # タグ処理 $val =~ s/&/&amp;/g; $val =~ s/"/&quot;/g; $val =~ s/</&lt;/g; $val =~ s/>/&gt;/g; # 改行処理 if ($key eq "comment") { $val =~ s/\r\n/<br>/g; $val =~ s/\r/<br>/g; $val =~ s/\n/<br>/g; } else { $val =~ s/\r//g; $val =~ s/\n//g; } } $in{$key} = $val; } $mode = $in{'mode'}; $page = $in{'page'}; } (下のサブルーチンに入る前に上の&decodeを実行) #----------------# # ログ書込処理 # #----------------# sub regist { # 時間を取得 &get_time; # ログを開く open(IN,"$logfile") || &error("Open Error : $logfile"); @lines = <IN>; close(IN); # 記事NO処理 $top = shift(@lines); ($no,$ip,$time2) = split(/<>/, $top); $no++; # ファイル添付処理 if ($in{'upfile1'}) { &UpFile1; } if ($in{'pdffile'}) { &pdffileup; } ~ (ここからログに書き込み) という感じです。
argius

2016/08/24 06:13

念のため確認ですが、 .imgというディレクトリーは作ってあるんですよね? それでダメなら...うーん、見えないコードが多すぎて判断が付かないですね。 teratakaさんの2016/08/23 13:11のコメントにある ファイルアップロードだけの処理は、 私の借りているレンタルサーバーで確認しましたが、正常に動作しました。 このコードだけの処理を実行して上手く行かないようなら、 何か環境に問題があるかも知れませんね。 サーバーのPerlのバージョンは分かりますか? レンタルサーバーの情報に書かれていると思います。
terataka

2016/08/25 01:13

返答ありがとうございます。 >.imgというディレクトリーは作ってあるんですよね? ./imgに書き換えてはいますが、cgiと同ディレクトリにimgディレクトリはちゃんとあります。また、パーミッションも適切に設定してます。(if ($in{'upfile1'}) { &UpFile1; } でアップした画像は同じ場所にきちんとアップされています) perlのバージョンは、v5.10.1でした。 他のcgiに組み込まず単体でアップcgiを置けばちゃんと動作するので、ベースとなるCGIが古すぎでどこかでうまくいってないとかなんでしょうね… (require './jcode.pl';require './cgi-lib.pl';とかを使ってるのも問題だったりするのかなぁ…) とりあえず次点の方法として、 pdf以外のデータをアップ。pdfをアップする必要がある時はチェックボックス(type="checkbox" name="pdf")にチェックを入れる。 ↓ データアップ完了画面で、フォームのpdfにチェックがあったらpdfアップフォームを表示する。 ↓ 別cgiでpdfをアップ としてます。 手間が少しかかりますが、これで全データがアップ出来ないわけではないので、今回はこれで良しとしたいと思います。
argius

2016/08/25 01:31

バージョン、思ったより新しいですね。 (Perl5の最新は5.24ですが、私のサーバーは5.8ですので...) > 古すぎでどこかでうまくいってないとかなんでしょうね… それが原因かどうかは分かりませんが、Perl4時代のコードが散見されるので、Perl5.8以降の(5.8で大きく変わった)書き方を覚えられたほうが良いでしょうね。 すぐには無理だと思うので、そういう状態であるということだけ気に留めておいてください。 例として、jcode.plはそのバージョンならEncode.pmというのがあるのでそちらを使えば良いはずです。互換性は無いので、使っているところは書き換えが必要ですが。 cgi-lib.plも何かで置き換えられそうです。 ログの処理もモジュールでできると思いますし。 ReadParseがパラメーターを取得して%inに格納する処理なら、 これの代わりに %in = $q->Vars(); # $qはCGIモジュールのオブジェクト だけでだいたい同じになります。 > 手間が少しかかりますが、これで全データがアップ出来ないわけではないので、今回はこれで良しとしたいと思います。 力及ばず、すみません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問