Mojo-Weixin/lib/Mojo/Weixin/Message/Remote/_upload_media.pm
use strict;
use File::Temp;
use File::Basename ();
use Mojo::Util ();
use POSIX ();
use Mojo::Weixin::Const qw();
sub Mojo::Weixin::_upload_media {
my $self = shift;
my $msg = shift;
my $callback = shift;
my $max_media_size = $self->media_size_max // 20 * 1024 *1024;
if(!defined $msg or (!defined $msg->media_data and !defined $msg->media_path)){
$self->error("无效的media");
$callback->($msg,"invaild media");
return;
}
$self->steps(
sub{
my $delay = shift;
my $end = $delay->begin(0,);
if(defined $msg->media_data){
my $r = sub{my $r = sprintf "%.3f", rand();$r=~s/\.//g;return $self->now() . $r;}->();
$msg->media_ext("dat") if not defined $msg->media_ext;
$msg->media_name($r . "." . $msg->media_ext) if not defined $msg->media_name;
$msg->media_path($msg->media_name) if not defined $msg->media_path;
$msg->media_size(length($msg->media_data)) if not defined $msg->media_size;
if($msg->media_size > $max_media_size){
$self->error("媒体[".$msg->media_path."]大小超出限制( 最大值 $max_media_size bytes)");
$end->($msg,"media size exceed, maximum size is $max_media_size bytes");
return;
}
$msg->media_mime('application/octet-stream') if not defined $msg->media_mime;
$msg->media_mtime(time) if not defined $msg->media_mtime;
$end->($msg);
return;
}
elsif($msg->media_path=~/^https?:\/\/.*?([^\/]+)$/){
my $name = $1;
my $ext = '';
$ext = $1 if $name =~ /\.([^\.]+)$/;
$self->http_get($msg->media_path,sub{
my($body,$ua,$tx) = @_;
return if not defined $body;
my($mtime,$mime,$size) = ($tx->res->headers->last_modified || time, $tx->res->headers->content_type|| 'application/octet-stream',$tx->res->headers->content_length || length($body));
$mime=~s/;.*$//;
if(not $ext){
$ext = $mime=~/^image\/jpe?g/i ? "jpg"
: $mime=~/^image\/png/i ? "png"
: $mime=~/^image\/bmp/i ? "bmp"
: $mime=~/^image\/gif/i ? "gif"
: $mime=~/^text\/plain/i ? "txt"
: $mime=~/^text\/html/i ? "html"
: $mime=~/^text\/json/i ? "json"
: $mime=~/^application\/json/i ? "json"
: $mime=~/^video\/mp4/i ? "mp4"
: $mime=~/^audio\/mp3/i ? "mp3"
: $mime=~/^audio\/mpeg/i ? "mp3"
: $mime=~/^application\/json/i ? "json"
: "dat"
;
}
$msg->media_size($size) if not defined $msg->media_size;
if($msg->media_size > $max_media_size){
$self->error("媒体[".$msg->media_path."]大小超出限制( 最大值 $max_media_size bytes)");
$end->($msg,"media size exceed, maximum size is $max_media_size bytes");
return;
}
$msg->media_name($name) if not defined $msg->media_name;
$msg->media_mime($mime) if not defined $msg->media_mime;
$msg->media_mtime($mtime) if not defined $msg->media_mtime;
$msg->media_data($body) if not defined $msg->media_data;
$msg->media_ext($ext) if not defined $msg->media_ext;
$end->($msg);
});
return;
}
else{
if(not -f $msg->media_path){
$self->error("无效的文件路径");
$callback->($msg,"invaild media_path");
return;
}
my %mime_map = (
jpeg => 'image/jpeg',
jpg => 'image/jpeg',
gif => 'image/gif',
bmp => 'image/bmp',
png => 'image/png',
mp3 => 'audio/mp3',
mp4 => 'video/mp4',
);
my $mime_reg = join "|",keys %mime_map;
eval{
my $size = (stat($msg->media_path))[7];
if($size > $max_media_size){
$self->error("媒体[".$msg->media_path."]大小超出限制( 最大值 $max_media_size bytes)");
$end->($msg,"media size exceed, maximum size is $max_media_size bytes");
return;
}
my $name = File::Basename::basename($msg->media_path);
my $data = $self->slurp($msg->media_path);
my $mtime = (stat($msg->media_path))[9];
my $mime = 'application/octet-stream';
if($name=~/\.($mime_reg)$/) {
$mime = $mime_map{$1};
}
my $ext = '';
$ext = $1 if $name =~ /\.([^\.]+)$/;
$ext = 'dat' if not $ext;
$msg->media_name($name) if not defined $msg->media_name;
$msg->media_size($size) if not defined $msg->media_size;
$msg->media_mime($mime) if not defined $msg->media_mime;
$msg->media_mtime($mtime) if not defined $msg->media_mtime;
$msg->media_data($data) if not defined $msg->media_data;
$msg->media_ext($ext) if not defined $msg->media_ext;
$end->($msg);
};
if($@){
$self->error("读取媒体文件" .$msg->media_path . "错误: $@");
$end->($msg,"media file read error: $@");
return;
}
}
},
sub {
my $delay = shift;
my $msg = shift;
my $err = shift;
if($err){
$callback->($msg,$err);
return;
}
$msg->on(_upload_media_chunk_over=>sub{
my($msg,$err) = @_;
if($err){#上传失败
$msg->client->error("媒体[".$msg->media_path."]上传失败: $err");
$callback->($msg,$err);
return;
}
elsif($msg->media_chunk == $msg->media_chunks){#最后一个分片上传完毕
$callback->($msg);
return;
}
else{#继续上传下一个分片
$msg->emit("_upload_media_chunk");
}
}) if !$msg->has_subscribers('_upload_media_chunk_over');
$msg->on(_upload_media_chunk=>sub{
my $msg = shift;_upload_chunk($msg->client,$msg,sub{my $msg=shift;$msg->emit(_upload_media_chunk_over=>@_)});
}) if !$msg->has_subscribers('_upload_media_chunk');;
$msg->emit("_upload_media_chunk");
}
);
}
sub _upload_chunk{
my ($self,$msg,$callback) = @_;
if(!defined $msg->media_chunks){
$msg->media_chunks(POSIX::ceil($msg->media_size/$self->media_chunk_size));
}
if(!defined $msg->media_clientid){
#$msg->media_clientid($self->now);
$msg->media_clientid(sub{my $r = sprintf "%.3f", rand();$r=~s/\.//g;return $self->now() . $r;}->());
}
$self->debug("正在上传媒体[". $msg->media_path ."],当前进度:" . ($msg->media_chunk+1) . "/" . $msg->media_chunks);
my $uploadmediarequest = {
BaseRequest => {
DeviceID => $self->deviceid,
Sid => $self->wxsid,
Skey => $self->skey,
Uin => $self->wxuin,
},
ClientMediaId => $msg->media_clientid,
TotalLen => $msg->media_size,
StartPos => 0,
DataLen => $msg->media_size,
MediaType => 4,
FromUserName => $msg->sender_id,
ToUserName => ($msg->type eq "group_message"?$msg->group_id:$msg->receiver_id),
UploadType=> 2,
};
if(!defined $msg->media_md5 and defined $msg->media_data){ $msg->media_md5( Mojo::Util::md5_sum($msg->media_data ))}
$uploadmediarequest->{FileMd5} = $msg->media_md5 if defined $msg->media_md5;
if(not defined $msg->media_type){
my $media_type = $msg->media_mime=~/^image\/gif/i ? "emoticon"
: $msg->media_mime=~/^video\/(mp4|mpeg)/i ? "video"
# : $msg->media_mime=~/^audio\/mp3/i ? "voice"
: $msg->media_mime=~/^image\// ? "image"
: "file"
;
$msg->media_type($media_type);
}
$msg->media_code($Mojo::Weixin::Const::KEY_MAP_MEDIA_CODE{$msg->media_type} || 6);
my $msg_content = $msg->media_type eq "image" ? "[图片]"
: $msg->media_type eq "emoticon" ? "[表情]"
: $msg->media_type eq "video" ? "[视频]"
: $msg->media_type eq "microvideo"? "[小视频]"
: $msg->media_type eq "voicce" ? "[语音]"
: $msg->media_type eq "file" ? "[文件]"
: "[文件]"
;
$msg->content($msg_content . "(" . $msg->media_path . ")");
$self->http_post(
'https://file.' . $self->domain .'/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json',
{ json=>1,
Referer=>'https://' . $self->domain . '/',
ua_request_timeout => 120,
ua_inactivity_timeout => 120,
},
form=>{
id=>'WU_FILE_0',
name=>$msg->media_name,
type=>$msg->media_mime,
($msg->media_chunks>1?(chunks=>$msg->media_chunks):()),
($msg->media_chunks>1?(chunk=>$msg->media_chunk):()),
lastModifiedDate=>POSIX::strftime('%a %b %d %Y %H:%M:%S GMT+0800 (中国标准时间)',gmtime($msg->media_mtime)),
size=>$msg->media_size,
mediatype=>(
$msg->media_type eq "image" ? "pic"
: ($msg->media_type eq "video" or $msg->media_type eq "microvideo") ? "video"
: "doc"
),
uploadmediarequest=>$self->to_json($uploadmediarequest),
webwx_data_ticket=>$self->search_cookie("webwx_data_ticket"),
pass_ticket => $self->pass_ticket,
filename =>{
content=>substr($msg->media_data,$self->media_chunk_size * $msg->media_chunk,$self->media_chunk_size),
filename=>$msg->media_name,
'Content-Type' => $msg->media_mime // 'application/octet-stream',
},
},
sub{
my $json = shift;
if(not defined $json){
$callback->($msg,"media upload failure " . $msg->media_chunk. "/" . $msg->media_chunks );
return;
}
else{
$msg->media_chunk( 1+$msg->media_chunk );
$msg->media_id(defined $msg->media_code?$json->{MediaId} . ":" . $msg->media_code : $json->{MediaId}) if $json->{MediaId};
$callback->($msg);
return;
}
}
);
}
1;