3 namespace Drupal\migrate\Plugin\migrate\process;
5 use Drupal\Core\File\FileSystemInterface;
6 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
7 use Drupal\migrate\MigrateException;
8 use Drupal\migrate\MigrateExecutableInterface;
9 use Drupal\migrate\Row;
10 use GuzzleHttp\Client;
11 use Symfony\Component\DependencyInjection\ContainerInterface;
14 * Downloads a file from a HTTP(S) remote location into the local file system.
16 * The source value is an array of two values:
17 * - source URL, e.g. 'http://www.example.com/img/foo.img'
18 * - destination URI, e.g. 'public://images/foo.img'
20 * Available configuration keys:
21 * - file_exists: (optional) Replace behavior when the destination file already
23 * - 'replace' - (default) Replace the existing file.
24 * - 'rename' - Append _{incrementing number} until the filename is
26 * - 'use existing' - Do nothing and return FALSE.
27 * - guzzle_options: (optional)
28 * @link http://docs.guzzlephp.org/en/latest/request-options.html Array of request options for Guzzle. @endlink
40 * This will download source_url to destination_uri.
51 * This will download source_url to destination_uri and ensure that the
52 * destination URI is unique. If a file with the same name exists at the
53 * destination, a numbered suffix like '_0' will be appended to make it unique.
55 * @MigrateProcessPlugin(
59 class Download extends FileProcessBase implements ContainerFactoryPluginInterface {
62 * The file system service.
64 * @var \Drupal\Core\File\FileSystemInterface
66 protected $fileSystem;
69 * The Guzzle HTTP Client service.
71 * @var \GuzzleHttp\Client
73 protected $httpClient;
76 * Constructs a download process plugin.
78 * @param array $configuration
79 * The plugin configuration.
80 * @param string $plugin_id
82 * @param mixed $plugin_definition
83 * The plugin definition.
84 * @param \Drupal\Core\File\FileSystemInterface $file_system
85 * The file system service.
86 * @param \GuzzleHttp\Client $http_client
89 public function __construct(array $configuration, $plugin_id, array $plugin_definition, FileSystemInterface $file_system, Client $http_client) {
91 'guzzle_options' => [],
93 parent::__construct($configuration, $plugin_id, $plugin_definition);
94 $this->fileSystem = $file_system;
95 $this->httpClient = $http_client;
101 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
106 $container->get('file_system'),
107 $container->get('http_client')
114 public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
115 // If we're stubbing a file entity, return a uri of NULL so it will get
116 // stubbed by the general process.
117 if ($row->isStub()) {
120 list($source, $destination) = $value;
122 // Modify the destination filename if necessary.
123 $final_destination = file_destination($destination, $this->configuration['file_exists']);
125 // Reuse if file exists.
126 if (!$final_destination) {
130 // Try opening the file first, to avoid calling file_prepare_directory()
131 // unnecessarily. We're suppressing fopen() errors because we want to try
132 // to prepare the directory before we give up and fail.
133 $destination_stream = @fopen($final_destination, 'w');
134 if (!$destination_stream) {
135 // If fopen didn't work, make sure there's a writable directory in place.
136 $dir = $this->fileSystem->dirname($final_destination);
137 if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
138 throw new MigrateException("Could not create or write to directory '$dir'");
140 // Let's try that fopen again.
141 $destination_stream = @fopen($final_destination, 'w');
142 if (!$destination_stream) {
143 throw new MigrateException("Could not write to file '$final_destination'");
147 // Stream the request body directly to the final destination stream.
148 $this->configuration['guzzle_options']['sink'] = $destination_stream;
151 // Make the request. Guzzle throws an exception for anything but 200.
152 $this->httpClient->get($source, $this->configuration['guzzle_options']);
154 catch (\Exception $e) {
155 throw new MigrateException("{$e->getMessage()} ($source)");
158 return $final_destination;