我想你对PHP和文件上传还不是很熟悉。因此,我从一些基础知识开始,以一个简单的类结束。
如果您尚未阅读basics about file uploads 使用PHP,现在就开始。没有人会在这里为你解释,这是离题的。
让我们从基本的HTML表单开始
if( empty( $_FILES ) ) {
global $pagenow;
$url = admin_url( $pagenow );
?>
<!-- The data encoding type, enctype, MUST be specified as below -->
<form enctype="multipart/form-data" action="<?php echo $url; ?>" method="POST">
<!-- MAX_FILE_SIZE must precede the file input field -->
<input type="hidden" name="MAX_FILE_SIZE" value="30000" />
<!-- Name of input element determines name in $_FILES array -->
Send this file: <input name="userfile" type="file" />
<input type="submit" value="Send File" />
</form>
<?php
} else {
$imageupload = new File_Upload();
$attachment_id = $imageupload->create_attachment();
var_dump( $attachment_id );
}
这很简单。如果超全局数组
$_FILES
为空,表示没有上载文件,请显示上载表单。如果不为空,请处理上载。我使用
admin_url()
以及
$pagenow
var创建操作url。如果您在前端使用上载表单,则必须使用
home_url()
或者类似的东西。
用HTML发送文件后,您必须处理上载的文件。这将在File_Upload
班
class File_Upload
{
/**
* Index key from upload form
* @var string
*/
public $index_key = \'\';
/**
* Copy of superglobal array $_FILES
* @var array
*/
public $files = array();
/**
* Constructor
* Setup files array and guess index key
*/
public function __construct(){
if ( isset( $_FILES ) && ! empty( $_FILES ) ) {
$this->files = $_FILES;
$this->guess_index_key();
}
}
/**
* Set/overwrites the index key
* Converts $name with type casting (string)
*
* @param string $name Name of the index key
* @return string ::name Name of the stored index key
*/
public function set_field_name_for_file( $name = \'\' ) {
$this->index_key = ( ! empty( $name ) ) ? (string) $name : \'\';
return $this->index_key;
}
/**
* Converts uploaded file into WordPress attachment
*
* @return boolean Whether if the attachment was created (true) or not (false)
*/
public function create_attachment(){
// move the uploaded file from temp folder and create basic data
$imagedata = $this->handle_uploaded_file();
// if moving fails, stop here
/*
* For Production
* Set and return an error object with WP_Error()
*/
if ( empty( $imagedata ) )
return false;
/*
* For Production
* Check if $imagedata contains the expected (and needed)
* values. Every method could fail and return malicious data!!
*/
$filename = $imagedata[\'filename\'];
// create the attachment data array
$attachment = array(
\'guid\' => $imagedata[\'url\'] . \'/\' . $filename,
\'post_mime_type\' => $imagedata[\'type\'],
\'post_title\' => preg_replace(\'/\\.[^.]+$/\', \'\', $filename ),
\'post_content\' => \'\',
\'post_status\' => \'inherit\'
);
// insert attachment (posttype attachment)
$attach_id = wp_insert_attachment( $attachment, $filename );
// you must first include the image.php file
// for the function wp_generate_attachment_metadata() to work
require_once( ABSPATH . \'wp-admin/includes/image.php\' );
/*
* For Production
* Check $attach_data, wp_generate_attachment_metadata() could fail
* Check if wp_update_attachment_metadata() fails (returns false),
* return an error object with WP_Error()
*/
$attach_data = wp_generate_attachment_metadata( $attach_id, $filename );
wp_update_attachment_metadata( $attach_id, $attach_data );
return $attach_id;
}
/**
* Handles the upload
*
* @return array $return_data Array with informations about the uploaded file
*/
protected function handle_uploaded_file() {
// get the basic data
$return_data = wp_upload_dir();
// get temporary filepath and filename from $_FILES ($this->files)
$tmp_file = ( isset( $this->files[$this->index_key] ) && ! empty( $this->files[$this->index_key] ) ) ?
(string) $this->files[$this->index_key][\'tmp_name\'] : \'\';
$tmp_name = ( isset( $this->files[$this->index_key] ) && ! empty( $this->files[$this->index_key] ) ) ?
(string) $this->files[$this->index_key][\'name\'] : \'\';
// stop if something went wrong
if ( empty( $tmp_file ) )
return false;
// set filepath
$filepath = $return_data[\'filepath\'] = $return_data[\'path\'] . \'/\' . basename( $tmp_name );
// move uploaded file from temp dir to upload dir
move_uploaded_file( $tmp_file , $filepath );
// set filename
$filename = $return_data[\'filename\'] = basename( $filepath );
// set filetype
/*
* For Production
* You should really, really check the file extension and filetype on
* EVERY upload. If you do not, it is possible to upload EVERY kind of
* file including malicious code.
*/
$type = wp_check_filetype( $filename, null );
$return_data[\'file_ext\'] = ( isset( $type[\'ext\'] ) && ! empty( $type[\'ext\'] ) ) ?
$type[\'ext\'] : \'\';
$return_data[\'type\'] = ( isset( $type[\'type\'] ) && ! empty( $type[\'type\'] ) ) ?
$type[\'type\'] : \'\';
// return the results
return $return_data;
}
/**
* Try to fetch the first index from $_FILES
*
* @return boolean Whether if a key was found or not
*/
protected function guess_index_key() {
$keys = array_keys( $_FILES );
if ( ! empty( $keys ) ) {
$this->index_key = $keys[0];
return true;
}
return false;
}
}
这是一个基本类
not for production! 文件上载是一个非常敏感的话题,您必须自己验证和清理所有数据。否则,可能有人上传恶意代码并入侵您的服务器/博客/网站!
现在,一步一步地,在何时何地发生了什么。
通过创建类的实例,该类尝试创建超全局数组的副本$_FILES
. 如果使用超全局阵列,请不要触摸它们!也许代码的其他部分(插件或主题)也需要其中的数据。构造函数中发生的下一件事是,我们尝试猜测索引键。正如您所知,我们可以传递多个文件$_FILES
可以包含所有上载文件的数据。如果需要处理多个文件,请循环$_FILES
并将索引键设置为set_field_name_for_file()
// get the indexes from $_FILES
$keys = array_keys( $_FILES );
$upload_processor = new File_Upload();
foreach ( $keys as $key ) {
$upload_processor->set_field_name_for_file( $key );
$upload_processor->create_attachment();
}
set_field_name_for_file()
如果您想从HTML表单中pic一个特定字段,也需要使用,否则类将使用它可以找到的第一个索引。
下一步是处理上传的文件。方法create_attachment()
调用受保护的方法handle_uploaded_file()
, 您不必手动执行。这个handle_uploaded_file()
方法只需收集和设置一些所需的路径和文件名。
最后一步是创建附件。create_atatchment()
将设置所有需要的数据并为您创建附件。该方法返回ID ,以便您可以使用访问所有附件数据get_post( [id] )
$imageupload = new File_Upload();
$attachment_id = $imageupload->create_attachment();
[some other code]
$attachment = get_post( $attachment_id );
$image_url = $attachment->guid;
printf( \'<p><img src="%s"></p>\', $image_url );
Some words about error handling
我在生产环境的类代码中编写了一些提示。A我上面提到过,文件上传是一个非常敏感的话题。你必须检查、验证和清理一切。WordPress具有内置错误处理
WP_Error()
. 使用它!并检查上载是否成功
is_wp_error()
.
PHP可以设置一些错误消息,说明上传失败的原因。但是PHP不会返回清晰的文本消息,它会返回错误代码。将这些代码转换为明文的方法如下所示:
受保护的函数guess\\u upload\\u错误($err=0){
$errcodes = array(
\'Unknown error\',
\'The uploaded file exceeds the upload_max_filesize directive in php.ini.\',
\'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.\',
\'The uploaded file was only partially uploaded.\',
\'No file was uploaded.\',
\'Missing a temporary folder.\',
\'Failed to write file to disk.\',
\'A PHP extension stopped the file upload. PHP does not provide a way to ascertain which extension caused the file upload to stop; examining the list of loaded extensions with phpinfo() may help.\'
);
return ( isset( $errcodes[$err] ) ) ?
$errcodes[$err] : \'Unknown error\';
}
您必须检查上载是否成功,这可能如下所示(在方法中handle_upload_file()
)
// stop if something went wrong
if ( empty( $tmp_file ) ) {
$code = ( isset( $this->files[$this->index_key][\'error\'] ) ) ?
$this->files[$this->index_key][\'error\'] : 0;
$msg = $this->guess_upload_error( $code );
return new WP_Error( \'uploaderror\', \'Upload failed with message: \' . $msg );
}
然后在调用类时必须处理错误
if ( empty( $_FILES ) ) {
global $pagenow;
$url = admin_url( $pagenow );
?>
<!-- HTML form-->
<?php
} else {
$imageupload = new File_Upload();
$attachment_id = $imageupload->create_attachment();
if ( is_wp_error( $attachment_id ) ) {
echo \'<ol>\';
foreach ( $attachment_id->get_error_messages() as $err )
printf( \'<li>%s</li>\', $err );
echo \'</ol>\';
} else {
// do something with the created attachment
$attachment = get_post( $attachment_id );
$image_url = $attachment->guid;
printf( \'<p><img src="%s"></p>\', $image_url );
}
}
Always remember: 永远不要让失败的上传不被处理!