关于如何使用fuelphp文件上传的备注
你好。
我是Mandai,负责Wild 开发团队。
使用 Fuelphp 简单地上传文件非常简单,如果您从文档中复制并粘贴代码,那么它基本上就可以工作。
不过有很多东西不知道怎么做,所以总结了一下。
准备文件上传(HTML + CSS + JS)
在实现文件上传之前,需要准备好HTML等前端材料。
让我们从 HTML 开始。
它是表单标签内的输入标签。
1 | < form id = "form" name = "form" method = "post" enctype = "multipart/form-data" >< input type = "file" name = "f" class = "hidden" id = "f" multiple = "multiple" >< label for = "f" >< span class = "btn" >文件选择</ span >< span class = "selected" id = "f_selected" >未选择</ span ></ label ></ form > |
接下来,CSS。
1 | input. hidden { /* 显示:无;*/ } 标签 > span.btn { 显示:内联块;边框: 1px 实心 #999 ; 字体: 13.333px 行高: 21px ;左: 0.25em ;右内边距: 0.25em ;背景:-webkit-线性渐变( #fcfcfc , #dcdcdc );背景:线性渐变( #fcfcfc , #dcdcdc ); span.btn:active { 背景:-webkit-线性渐变( #dcdcdc , #fcfcfc ); 背景:线性渐变( #dcdcdc , #fcfcfc ); } 标签 > span.btn:hover { 边框: 1px 实心 #666 ; } 标签 > span.selected { 显示:内联块; 字体: 13.333px ; 行高: 21px ; } 标签 { -moz-user-select:无; ; -webkit-用户选择:无; |
最后但并非最不重要的是 JavaScript。
1 | ( function (){ var f = document.getElementById( 'f' ); var label_selected = document.getElementById(f.id + '_selected' ); f.addEventListener( 'change' , function (){ if (f.files .length == 0){ label_selected.textContent = '未选择' ; } else if (f.files.length == 1){ label_selected.textContent = f.files[0].name } else { label_selected .textContent = f.files.length + '文件' } }); |
当您在浏览器中显示它时,它看起来如下所示。推荐使用Chrome。
当你看到它的时候,原因就很明显了。
有两个文件上传按钮。
按任一按钮都会起作用。
左边的是实际产品,右边的是使用标签标签的复制品。
我只是想这么做。
表单部分在浏览器之间的显示不同,而且我还想让文件上传按钮本身符合设计,所以这个技巧很有用。
使用 CSS + JS 大致匹配行为和外观,这次我使它类似于 chrome 表单组件。
如果您将“display: none”添加到 CSS 中的输入标记中,它的行为与普通表单没有什么不同。
(已注释掉)
重要的是不要忘记表单标记中的 enctype 属性。
至此,前期准备工作已经完成。
Fuelphp的实现(准备)
在实施之前,我们将进行各种设置并准备模型。
由于 Fuelphp 是配置优先的,因此可以通过配置文件极大地改变其行为。
首先,在上传时,准备一个用于上传的配置文件。
源配置文件位于“fuel/core/config/upload.php”中,因此将其复制到“fuel/app/config”下并更改设置。
需要设置的项目是path。
至于保存目的地,如果将其设置为公共,下载机制会更容易,但如果您知道带有个人信息的PDF的URL,则可以随意下载,因此最好设置它低于公众。
如果需要控制访问,请创建并指定一个目录,例如公共层次结构中的文件。
在这种情况下,将无法从外部访问它,并且您将无法下载它,因此您会担心开销,但您可以通过创建单独的控制器来创建下载 URL 来处理此问题。
这部分和文件上传有点不同,所以我想下次再写。
这次,创建一个与public同级的files目录(直接在项目根目录下)并保存文件。
另外,对于要保存的文件名,我在配置文件中将 randomize 设置为 true 并使用 32 个字符的 MD5 哈希值。
修改后的 config/upload.php 现在如下所示。
1 | return array ( 'path' => '/var/www/html/test/files' , 'randomize' => true, ); |
接下来,创建模型。
由于要保存的文件名加上了哈希值,原来的文件名已经不知道了,所以它的主要作用就是保存。
创建上传表进行存储的SQL语句如下。
1 | 创建表 `uploads` (`id` int (11) unsigned NOT NULL AUTO_INCRMENT、`origin_name` varchar (256) NOT NULL 、`file_name` varchar (256) NOT NULL 、`created_at` 时间戳不为 NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP , `updated_at` 时间戳 NOT NULL DEFAULT '0000-00-00 00:00:00' , PRIMARY KEY (`id`), KEY `idx_file_name` (`file_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
执行此 SQL 后,最快的方法是使用 oil 命令创建模型,如下所示。
1 | php /path/to/oil r fromdb:模型上传 |
另外,由于我们将使用 ORM 包,因此我们需要修改 config.php 中的always_load 项。
如果取消注释包部分,它将自动加载。
Fuelphp(控制器)的实现
我们将在发布数据时实现控制器的行为。
现在控制器中编写的源代码如下。
1 | function post_index() { $data = []; $this ->template->title = 'test' ; /* * 随机化处理后的文件名如下 * [0-9a-f] { 32}.[ext] * 由于原始文件名将保存在单独的字段中,因此我们将介绍一个在保存之前将文件名更改为仅哈希值的过程 */ Upload::register( 'before' , function ( &$ file) { $file [ 'filename' ] = substr ( $file [ 'filename' ], 0, 32); $file [ 'path' ] .= $file [ 'filename' ][0]. '. $file[' 文件名 '][1]. }); if (Upload::is_valid()) { 上传::save(); foreach (Upload::get_files() as $file) { Model_Upload: :add($file); } } foreach (Upload::get_errors() as $file) { // 错误处理 } $this->template->content = View::forge(' upload/index', $数据) ; } |
如果在config中的upload.php中将“auto_process”设置为true(默认),则不需要显式编写Upload::process()。
文档,如果在 'auto_process' => true 时执行 Upload::process(),Upload::process() 将执行两次。
您可以使用 Upload::process() 覆盖配置,因此如果在多个位置描述文件上传过程,则最好将 auto_process 设置为 false 并每次执行 Upload::process() 。
Upload::register() 方法用于在保存文件之前中断处理。
第一个参数应该是“验证”、“之前”和“之后”之一。
这次我选择之前,因为我想在保存之前更改文件名和保存路径。
如果 config/upload.php 中的 randomize 设置为 true,则文件名将是 32 个字符的哈希值,但具有原始扩展名。
为了完全统一文件名为32个字符,我们添加了一个将文件名更改为哈希值的过程。
另外,关于保存目的地,使用哈希值的第一个和第二个字符创建目录并重置为路径。
如果上传的文件数量较少,这并不是特别必要,但如果上传的文件数量较多,或者存储了数万个文件,则读写响应会明显下降。
作为对此的回应,我们添加了处理以分配一个目录中的文件数量。
$file,它是Upload::register()中闭包中的一个参数,传递的是fuel/vendor/fuelphp/upload/src/File.php中编写的类,并且这个类似乎可以从外部访问。没办法做到这一点。
如果您在“之前”(保存之前)编辑此类,则可以操作保存路径和文件。
如果你转储 $file,它将包含如下内容:
1 | Fuel\Upload\File::__set_state( array ( 'container' => array ( 'element' => 'f' , 'filename' => 'e9477391cbeefc40e69f7e1bc24868d2' , 'name' => 'test.html' , 'type' => 'text/html' , 'tmp_name' => '/tmp/phpDsDmiL' , '错误' => 0, '大小' => 335, '扩展名' => 'html' , '基本名称' => '测试' , 'mimetype' => 'text/html' , 'path' => '/home/vagrant/workspace/test/files/e/9/' , ), 'index' => 0, 'errors' => array ( ), 'config' => array ( 'langCallback' => '\\Upload::lang_callback' , 'moveCallback' => NULL, 'max_size' => 0, 'max_length' => 0, 'ext_whitelist' = > array ( ), 'ext_blacklist' => 数组 ( ), 'type_whitelist' => 数组 ( ), 'type_blacklist' => 数组 ( ), 'mime_whitelist' => 数组 ( ), 'mime_blacklist' => 数组 ( ), “前缀”=>“”、“后缀”=>“”、“扩展名”=>“”、“随机化”=> true、“标准化”=> false、“normalize_separator”=>“_”、“change_case” =>假,“路径”=>“/ var /www/html/test/files”,“create_path”=>真,“path_chmod”=>511,“file_chmod”=>438,“auto_rename”=>真, 'new_name' => false, 'overwrite' => false, ), 'isValidated' => true, 'isValid' => true, 'callbacks' => array ( 'before_validation' => array ( ), 'after_validation' = > array ( ), 'before_save' => array ( 0 => Closure::__set_state( array ( )), ), 'after_save' => array ( ), ), )) |
有点混乱,但是用Upload::get_files()获取的文件信息只是Fuel\Upload\File的容器部分,而不是上面的类。
另外,即使你改变了Upload::get_files()中的信息,实际的文件信息也无法改变,所以看来改变上传文件信息的唯一方法就是在before事件中。
该区域文档的注册方法部分中有详细介绍
就是这样。