読者です 読者をやめる 読者になる 読者になる

Mojo::Uploadのmove_toは必ずしもmvではない

mojolicious

Mojo::Uploadのmove_toの実態は、メンバ変数のassetのmove_toに委譲しています。

sub move_to {
  my $self = shift;
  $self->asset->move_to(@_);
  return $self;
}

このassetは「Mojo::Asset::Memory」か「Mojo::Asset::File」のインスタンスが入ります。
どちらのインスタンスになるかは、アップロードされたファイルの容量によって決まります。

Mojo::Asset::Fileのmove_toの実装は、以下のように期待通りのmove。

  # Move file and prevent clean up
  my $from = $self->path;
  move($from, $to) or croak qq{Can't move file "$from" to "$to": $!};
  return $self->path($to)->cleanup(0);


しかし、Mojo::Asset::Memoryは、以下のようにメモリーからファイルに書き出す処理となっています。

  spurt $self->{content}, $to;

spurtはMojo::Utilで実装されている関数で、内部ではsyswriteを呼び出しています。


したがって、書きだしている途中でファイルが読み込まれるアプリだと不具合が出ます。
これに対応するために、以下のようにM::A::Fileに一回書きだすようにします。

  unless ($upload->asset->is_file) {
      my $file = Mojo::Asset::File->new;
      $file->add_chunk($self->slurp);
      $upload->asset($file);
  }

  $upload->move_to($to);


この他に、Mojo::Asset::Fileが来るもんだと思って、$upload->asset->handleなどでファイルハンドルを得ようとするとエラーになったりするので注意。
is_fileで確かめるようにしたほうが良い。