Drupal8重命名上传的中文名文件
来源:https://blog.mulin.tech/archives/20
完整的模块代码文件在Coding.net上,想直接使用的请前往下载:https://coding.net/u/yamus/p/chinese_rename/git/tree/master
最近吧Drupal8部署到Windows平台的时候,发现上传中文名的文件会出错。找了相关的模块,装上之后错误依旧。
后来发现错误是出在move_uploaded_file这一步的,也就是说必须在文件上传到服务器后,Drupal保存文件之前修改文件名,才能避免错误,于是仔细查看Drupal8关于文件处理方面的代码。发现表单的文件字段有一个#value_callback属性,可以设置文件上传后调用的方法。于是把这个属性替换成我自己写的方法。
首先建立一个模块,详细步骤就不说了。使用 hook_form_alter 修改字段的 #value_callback属性。
1 function chinese_rename_form_alter(&$form, FormStateInterface $form_state, $form_id) { 2 # 检查文件名 3 # 获取表单内所有Element 4 $els = Element::children($form); 5 foreach($els as $elName) { 6 if(isset($form[$elName]['widget'])) { 7 foreach($form[$elName]['widget'] as $w => $widget) { 8 if(is_numeric($w)) { 9 if(@$form[$elName]['widget'][$w]['#type'] == 'managed_file') { 10 $form[$elName]['widget'][$w]['#value_callback'] = [ 11 'Drupal\chinese_rename\Controller\ChineseRenameController', 12 'rename_chinese_filename' 13 ]; 14 } 15 } 16 } 17 } 18 } 19 }
#value_callback属性是一个数组,前面是命名空间和类名,后面是方法名。
然后定义一个控制器,在模块目录的src/Controller目录下。控制器类里面定义一个处理文件名的方法,方法是静态的。这个方法接受三个参数。
1 class ChineseRenameController extends ControllerBase { 2 // 重命名中文文件名 3 public static function rename_chinese_filename(&$element, $input, FormStateInterface $form_state) { 4 # 如果提交新的文件 5 if(!is_numeric($input['fids'])) { 6 $form_field_name = implode('_', $element['#parents']); 7 $all_files = \Drupal::request()->files->get('files', array()); 8 9 // Make sure there's an upload to process. 10 if (empty($all_files[$form_field_name])) { 11 return NULL; 12 } 13 $file_upload = $all_files[$form_field_name]; 14 // Prepare uploaded files info. Representation is slightly different 15 // for multiple uploads and we fix that here. 16 $uploaded_files = $file_upload; 17 if (!is_array($file_upload)) { 18 $uploaded_files = array($file_upload); 19 } 20 $files = array(); 21 $destination = 'temporary://'; 22 $realPath = \Drupal::service('file_system')->realpath($destination); 23 foreach ($uploaded_files as $i => $file_info) { 24 # 把源文件对象替换成重命名过的文件对象 25 $originalName = $file_info->getClientOriginalName(); 26 if(preg_match("/[\x7f-\xff]/", $originalName)) { 27 $newName = md5($originalName) . '.' . $file_info->getClientOriginalExtension(); 28 # 新建一个上传文件对象 29 $newFile = new UploadedFile( 30 $file_info->getRealPath(), 31 $newName, 32 $file_info->getClientMimeType(), 33 $file_info->getClientSize(), 34 $file_info->getError() 35 ); 36 37 if(count($uploaded_files) <= 1) { 38 $uploaded_files = $newFile; 39 } else { 40 $uploaded_files[$i] = $newFile; 41 } 42 } 43 } 44 $all_files[$form_field_name] = $uploaded_files; 45 \Drupal::request()->files->set('files', $all_files); 46 } 47 48 # 调用默认文件处理方法 49 return \Drupal\file\Plugin\Field\FieldWidget\FileWidget::value($element, $input, $form_state); 50 } 51 }
这里使用MD5处理中文文件名,也可以换成其他的方法。文件保存好,启用模块,就可以在文件保存之前,修改文件名,解决中文文件名出错的问题了。