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\ProcessPluginBase;
10 use Drupal\migrate\Row;
11 use GuzzleHttp\Client;
12 use Symfony\Component\DependencyInjection\ContainerInterface;
15 * Downloads a file from a HTTP(S) remote location into the local file system.
17 * The source value is an array of two values:
18 * - source URL, e.g. 'http://www.example.com/img/foo.img'
19 * - destination URI, e.g. 'public://images/foo.img'
21 * Available configuration keys:
22 * - rename: (optional) If set, a unique destination URI is generated. If not
23 * set, the destination URI will be overwritten if it exists.
24 * - guzzle_options: (optional)
25 * @link http://docs.guzzlephp.org/en/latest/request-options.html Array of request options for Guzzle. @endlink
37 * This will download source_url to destination_uri.
48 * This will download source_url to destination_uri and ensure that the
49 * destination URI is unique. If a file with the same name exists at the
50 * destination, a numbered suffix like '_0' will be appended to make it unique.
52 * @MigrateProcessPlugin(
56 class Download extends ProcessPluginBase implements ContainerFactoryPluginInterface {
59 * The file system service.
61 * @var \Drupal\Core\File\FileSystemInterface
63 protected $fileSystem;
66 * The Guzzle HTTP Client service.
68 * @var \GuzzleHttp\Client
70 protected $httpClient;
73 * Constructs a download process plugin.
75 * @param array $configuration
76 * The plugin configuration.
77 * @param string $plugin_id
79 * @param mixed $plugin_definition
80 * The plugin definition.
81 * @param \Drupal\Core\File\FileSystemInterface $file_system
82 * The file system service.
83 * @param \GuzzleHttp\Client $http_client
86 public function __construct(array $configuration, $plugin_id, array $plugin_definition, FileSystemInterface $file_system, Client $http_client) {
89 'guzzle_options' => [],
91 parent::__construct($configuration, $plugin_id, $plugin_definition);
92 $this->fileSystem = $file_system;
93 $this->httpClient = $http_client;
99 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
104 $container->get('file_system'),
105 $container->get('http_client')
112 public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
113 // If we're stubbing a file entity, return a uri of NULL so it will get
114 // stubbed by the general process.
115 if ($row->isStub()) {
118 list($source, $destination) = $value;
120 // Modify the destination filename if necessary.
121 $replace = !empty($this->configuration['rename']) ?
124 $final_destination = file_destination($destination, $replace);
126 // Try opening the file first, to avoid calling file_prepare_directory()
127 // unnecessarily. We're suppressing fopen() errors because we want to try
128 // to prepare the directory before we give up and fail.
129 $destination_stream = @fopen($final_destination, 'w');
130 if (!$destination_stream) {
131 // If fopen didn't work, make sure there's a writable directory in place.
132 $dir = $this->fileSystem->dirname($final_destination);
133 if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
134 throw new MigrateException("Could not create or write to directory '$dir'");
136 // Let's try that fopen again.
137 $destination_stream = @fopen($final_destination, 'w');
138 if (!$destination_stream) {
139 throw new MigrateException("Could not write to file '$final_destination'");
143 // Stream the request body directly to the final destination stream.
144 $this->configuration['guzzle_options']['sink'] = $destination_stream;
147 // Make the request. Guzzle throws an exception for anything but 200.
148 $this->httpClient->get($source, $this->configuration['guzzle_options']);
150 catch (\Exception $e) {
151 throw new MigrateException("{$e->getMessage()} ($source)");
154 return $final_destination;