I am migrating from a Drupal 7 site hosted on Pantheon to Drupal 8.8.4. I am using the migrate upgrade module and running the commands through drush.

The public files all imported correctly, including the files in subdirectories. But I can't get any of the private files to work. I can't tell if the issue is the private files or the subdirectories (all of the private files are in subdirectories so it's hard to isolate).

When I run drush migrate:import upgrade_d7_file_private and check the migrate messages I get errors such as Cannot read from non-readable stream (http://[hostname]/sites/default/files/private/subdirectory/file.pdf)

I see the MigratePrivateFileTest.php test passes, so I assume it's not just private files only that are the problem. The test sets the private directory at sites/default/private, but Pantheon requires it to be at sites/default/files/private. Maybe that is the problem?

I tried creating a test for that scenario (file_private_path set to sites/default/files/private, no further subdirectories) and it fails. But I also tried creating a test for public files in subdirectories (which works on my site migration) and that test fails too so something is not correct about my test.

I'm attaching a patch with those two tests. If anyone has advice on making the MigrateFileSubdirectoryTest.php pass (since I believe the functionality is working) then I can make a tests that shows my problem better.

Issue fork drupal-3123350

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

sagannotcarl created an issue. See original summary.

sagannotcarl’s picture

quietone’s picture

@sagannotcarl, the source path for files is set in the migration, d7_file_private. You should only have to edit the configured migration to set the correct path for your system. It is 'source_base_path'. See https://git.drupalcode.org/project/drupal/-/blob/8.8.x/core/modules/file...

I hope that helps.

sagannotcarl’s picture

@quietone I have 'source_base_path' set in the configured migration, and that part is working. In the Cannot read from non-readable stream error the base path is there and appears to be correct.

By correct I mean that it's looking for the file at http://[base_path]/sites/default/files/private/subdirectory/file.pdf, the base path is correct, the private files live at sites/default/files/private, the subdirectory is correct and the filename is correct. Is there something else that would make it a non-readable stream? Something about the files being private?

Thanks so much for the help.

jim.shreds’s picture

Also seeing this on upgrade_d7_file_private.

sagannotcarl’s picture

@quietone I guess the other question is whether you think these tests I wrote are valid (i.e. properly demonstrate the bug)?

mrP’s picture

I had to patch core/modules/file/src/Plugin/migrate/source/d7/File.php to get around this type of issue when migrating a D7 (multisite installation type) to D8 (also multisite installation type).

  protected function initializeIterator() {
    $this->publicPath = $this->variableGet('file_public_path', NULL);
    $this->privatePath = $this->variableGet('file_private_path', NULL);
    $this->temporaryPath = $this->variableGet('file_temporary_path', '/tmp');
    return parent::initializeIterator();
  }

file_public_path was properly set on my D7 site, but it wasn't reading it in for some reason and kept defaulting to 'sites/default/files'. Example error:

File '/var/www/drupal7//sites/default/files/picture_282.png' does not exist.

file_public_path in D7 = sites/example.com/files

sagannotcarl’s picture

@mrP I tried your patch is it's not helping in my situation. For me the the prepareRow() function in .../d7/File.php is preparing my path correctly. At least it is the actual path of the file in the filesystem. My hypothosis is that because it's a private file the stream is expecting something else other than the actual filepath? Does anyone know if that is true?

If $row->setSourceProperty('filepath', $path); is correctly setting the actual path of the file on the d7 site for a private fle, why else would I get a Cannot read from non-readable stream error?

jim.shreds’s picture

im a dummy. forgot to remove the htaccess file from within the top level of the directory holding the private files.

sagannotcarl’s picture

@jim.shreds Is removing the htaccess file from the top level of the private files directory a requirement for migration? I haven't seen that mentioned anywhere but it does make sense if it's true. I assume that would cause a Cannot read from non-readable stream error?

jim.shreds’s picture

@sagannotcarl i have not seen it mentioned anywhere in any documentation. i only did it while quadruple checking file permissions inside of terminal. but yea in the end private files are now migrating.

Version: 8.8.4 » 8.8.x-dev

Core issues are now filed against the dev versions where changes will be made. Document the specific release you are using in your issue comment. More information about choosing a version.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

quietone’s picture

Status: Active » Postponed (maintainer needs more info)

@sagannotcarl, did the suggestion by jim.shreds to remove .htaccess allow you to migrate the files?

grevil’s picture

Could it perhaps be the same problem as described here?
https://drupal.stackexchange.com/questions/283175/files-migration-issue

We're currently running into the same situation where the OLD source directory is prepended to the file path,

The link describes it perfectly! Or is that a different issue? This is the closest one I could find.
Example:

source:
  plugin: d7_file
  scheme: private
  constants:
    source_base_path: '../_migration/files/old-private-files-source'
process:
  fid:
    -
      plugin: get
      source: fid
  filename:
    -
      plugin: get
      source: filename
  source_full_path:
    -
      plugin: concat
      delimiter: /
      source:
        - constants/source_base_path
        - filepath

Expected:
"../_migration/files/old-private-files-source/dirXYZ/fileABC.pdf"

Actual / current result:
"../_migration/files/old-private-files-source//my-old-drupal7-private-file-directory-path-from-db-variables/dirXYZ/fileABC.pdf"

The OLD results from private:// should NOT be used here, as the directory is environment specific. And if someone needs it, it can be added to source_base_path! But removing it (without changing the source db or hacking core like in #7) seems problematic!?

grevil’s picture

Status: Postponed (maintainer needs more info) » Active

Indeed the most simple, but dirty workaround is to delete the "file_privat_path" row from the copy of the source database variables table...
DELETE FROM variables WHERE name="file_private_path";

Tadaaa... problems gone! So I think this should be rated as major problem / bug for private files.

And like written in #7 the problem starts with these two functions in File.php:

  /**
   * {@inheritdoc}
   */
  protected function initializeIterator() {
    $this->publicPath = $this->variableGet('file_public_path', 'sites/default/files');
    $this->privatePath = $this->variableGet('file_private_path', NULL);
    return parent::initializeIterator();
  }

  /**
   * {@inheritdoc}
   */
  public function prepareRow(Row $row) {
    // Compute the filepath property, which is a physical representation of
    // the URI relative to the Drupal root.
    $path = str_replace(['public:/', 'private:/'], [$this->publicPath, $this->privatePath], $row->getSourceProperty('uri'));
    // At this point, $path could be an absolute path or a relative path,
    // depending on how the scheme's variable was set. So we need to shear out
    // the source_base_path in order to make them all relative.
    $path = preg_replace('#' . preg_quote($this->configuration['constants']['source_base_path']) . '#', '', $path, 1);
    $row->setSourceProperty('filepath', $path);
    return parent::prepareRow($row);
  }

In contrast to the private path, appending the old directory to public paths (or URL) is correct as that's still the folder to search in, while it's not for private files.

I'd suggest to make this configurable in the yml file by splitting the variables for example... something like this (especially for public files);

  source_full_path:
    -
      plugin: concat
      delimiter: /
      source:
        - constants/source_base_path
        - constants/source_base_files_directory
        - filepath

and removing the files directory variable from filepath!
Or simply documenting that source_base_path for private files should contain the /sites/default/files directory?

@quietone: Do you see the problem and what do you think?

grevil’s picture

Adding #3189876: Add documentation for file source plugins as related for documentation update after this was fixed.

anybody’s picture

Did the debugging with @Grevil and agree that for private files the combination with the private file path in "filepath" is a problem, while it leads to expected results for public files. Splitting the variables would be the most flexible way to solve this, I guess. "filepath" should never contain the replacement of the private:// or public:// scheme like it currently does.

Quite complex... especially not introducing a breaking change.

Currently I think it CAN only work if migration source and target are on the same machine, accessing the same directory structure or using an identical one for private files...

grevil’s picture

It's also worth reading the old issue: #2505283: Handle import of private files.

In #58 there was a constant "source_private_file_path" which got lost later on?

grevil’s picture

Version: 8.9.x-dev » 9.4.x-dev
grevil’s picture

As alternative to the SQL we added a dirty workaround in the issue fork to never prepend the file_private_path to the filepath. Of course that's not a real solution.

One could also discuss to split "filepath" variable from "filedir" and put them together in source_full_path for a non-BC!

  source_full_path:
    -
      plugin: concat
      delimiter: /
      source:
        - constants/source_base_path
        - filedir
        - filepath

@quietone I guess we need your feedback :)

grevil’s picture

Title: Handle private import of files from subdirectories » Private file migrations use of source file_private_path variable creates wrong directory structure
grevil’s picture

Title: Private file migrations use of source file_private_path variable creates wrong directory structure » D7 Private file migrations use of source file_private_path variable creates wrong directory structure

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

anybody’s picture

Just migrated another project and can confirm the issue still exists. The workaround in the MR fixes it. So we may proceed here with a cleaner fix, for example provide the private path in a separate migration variable or as source_base_path!

quietone’s picture

Issue tags: +migrate-d7-d8

Just updating tags,

grevil’s picture

StatusFileSize
new974 bytes

Here is a static patch for the time being.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

steve hanson’s picture

I have applied patch 29 but am still seeing the path being built incorrectly by concatenating the public and private paths together after also applying the patch for https://www.drupal.org/project/migrate_upgrade/issues/2921617#comment-15... so that the private path can be applied while setting up a drush migration. I'm somewhat bewildered that I cannot determine a way to make this work properly considering how long it has been broken.

Anybody changed the visibility of the branch 3123350-handle-private-import to hidden.

Anybody changed the visibility of the branch 3123350-handle-private-import to active.

Anybody changed the visibility of the branch 10.3.x to hidden.

Anybody changed the visibility of the branch 11.x to hidden.

anybody’s picture

Rerolled MR!1367 againt 11.x in MR!6919. Verified both are the same and equal to #29.

Applies cleanly against 11.x - but still a quickfix as documented.

anybody’s picture

StatusFileSize
new974 bytes

MR!6919 as patch attached!

grevil’s picture

Patch from #39 applies perfectly on 11.x, thx!