後來問了 Google 大神,請教先進,追了程式碼之後,還是不曉得該如何直接利用 MigrateDestinationMedia 來直接匯入多個檔案。
所以今天我研究了一下,寫了一個 FileMigration 類別來幹這件事。
有一些事要注意:
- 需要 simplehtmldom API 來做 html 碼的解析,以取出 img 標籤做進一步的處理
- 匯入的檔案的目錄結構,第一層必須是以 Drupal 的 language code 命名
- 可以在 prepareRow 或是 prepare 中呼叫 parseContentImage 來處理需要處理的欄位
- 需要在 prepare 中自行呼叫 MigrateDestinationMedia::rewriteImgTags 來改寫img 標籤
類別原始碼:
class FileMigration extends Migration {
const FAILURE = 0;
const SUCCESS = 1;
/**
* @var array
*/
protected $import_file_number = array(self::FAILURE => 0, self::SUCCESS => 0);
/**
* @param string $content
* @param string $language
* @return string
*/
protected function parseContentImage($content, $language) {
if (empty($content)) {
return '';
}
$html = str_get_html($content);
foreach ($html->find('img') as $image) {
$source = $image->attr['src'];
if (valid_url($source, TRUE)) {
continue;
}
$format_source = $this->formatImageSource($source, $language);
$file = $this->defaultFile();
$file->value = $format_source;
$file->destination_file = $format_source;
$file->field_file_image_alt_text = (empty($image->attr['alt'])) ? '' : $image->attr['alt'];
$file->field_file_image_title_text = (empty($image->attr['title'])) ? '' : $image->attr['title'];
try {
$this->processFile('image', $file);
$image->attr['src'] = $this->formatUrl($file->destination_dir . '/' . $format_source);
} catch (Exception $exception) {
$this->import_file_number[self::FAILUE]++;
$this->saveMessage($exception->getMessage());
}
}
return $html->save();
}
protected function formatUrl($uri) {
$scheme = file_uri_scheme($uri);
if (FALSE === $scheme) {
return ('/' == drupal_substr($uri, 0, 1)) ? $uri : '/' . drupal_encode_path($uri);
}
if ($scheme == 'http' || $scheme == 'https') {
return $uri;
}
$wrapper = file_stream_wrapper_get_instance_by_uri($uri);
if (FALSE == $wrapper) {
return '';
}
if (FALSE == is_subclass_of($wrapper, 'DrupalLocalStreamWrapper')) {
return $wrapper->getExternalUrl();
}
return '/' . $wrapper->getDirectoryPath() . '/' . file_uri_target($uri);
}
/**
* @param string $source
* @param string $language
* @return string
*/
protected function formatImageSource($source, $language) {
$part = explode('/', $source);
$und = FALSE;
while (true) {
$first = trim(reset($part));
if (empty($first) || ('..' == $first)) {
array_shift($part);
$und = TRUE;
} else {
if ($und) {
array_unshift($part, LANGUAGE_NONE);
} else {
array_unshift($part, $language);
}
break;
}
}
return implode('/', $part);
}
/**
* @param string $type
* @param stdClass $file
* @return array
*/
protected function processFile($type, stdClass $file) {
if (empty($file->type)) {
$file->type = $type;
}
if (FALSE == isset($file->uid)) {
$file->uid = 1;
}
if (isset($file->timestamp)) {
$timestamp = MigrationBase::timestamp($file->timestamp);
}
$file->preserve_files = FALSE;
$source = new MigrateFileUri((array) $file, $file);
$file = $source->processFile($file->value, $file->uid);
if (is_object($file) && isset($file->fid)) {
if (isset($timestamp)) {
db_update('file_managed')
->fields(array('timestamp' => $timestamp))
->condition('fid', $file->fid)
->execute();
$file->timestamp = $timestamp;
}
$this->import_file_number[self::SUCCESS]++;
$return = array($file->fid);
} else {
$this->import_file_number[self::FAILURE]++;
$return = FALSE;
}
return $return;
}
/**
* @return stdClass
*/
protected function defaultFile() {
$file = new stdClass();
$file->destination_dir = 'public://';
$file->urlencode = FALSE;
return $file;
}
protected function postImport() {
parent::postImport();
$total = array_sum($this->import_file_number);
if (0 >= $total) {
return;
}
$message = t("Imported !numitems files (!succeed succeed, !failed failed) - done with '!name'", array(
'!numitems' => $total,
'!succeed' => $this->import_file_number[self::SUCCESS],
'!failed' => $this->import_file_number[self::FAILURE],
'!name' => $this->machineName,
));
self::displayMessage($message, 'completed');
}
}