8.3.x file.module file_copy(FileInterface $source, $destination = NULL, $replace = FILE_EXISTS_RENAME)
8.0.x file.module file_copy(FileInterface $source, $destination = NULL, $replace = FILE_EXISTS_RENAME)
8.1.x file.module file_copy(FileInterface $source, $destination = NULL, $replace = FILE_EXISTS_RENAME)
8.2.x file.module file_copy(FileInterface $source, $destination = NULL, $replace = FILE_EXISTS_RENAME)
8.4.x file.module file_copy(FileInterface $source, $destination = NULL, $replace = FILE_EXISTS_RENAME)
4.6.x file.inc file_copy(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME)
4.7.x file.inc file_copy(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME)
5.x file.inc file_copy(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME)
6.x file.inc file_copy(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME)
7.x file.inc file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS_RENAME)

Copies a file to a new location and adds a file record to the database.

This function should be used when manipulating files that have records stored in the database. This is a powerful function that in many ways performs like an advanced version of copy().

  • Checks if $source and $destination are valid and readable/writable.
  • If file already exists in $destination either the call will error out, replace the file or rename the file based on the $replace parameter.
  • If the $source and $destination are equal, the behavior depends on the $replace parameter. FILE_EXISTS_REPLACE will error out. FILE_EXISTS_RENAME will rename the file until the $destination is unique.
  • Adds the new file to the files database. If the source file is a temporary file, the resulting file will also be a temporary file. See file_save_upload() for details on temporary files.


$source: A file object.

$destination: A string containing the destination that $source should be copied to. This must be a stream wrapper URI.

$replace: Replace behavior when the destination file already exists:

  • FILE_EXISTS_REPLACE - Replace the existing file. If a managed file with the destination name exists then its database entry will be updated. If no database entry is found then a new one will be created.
  • FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique.
  • FILE_EXISTS_ERROR - Do nothing and return FALSE.

Return value

File object if the copy is successful, or FALSE in the event of an error.

See also



Related topics

6 calls to file_copy()
FileCopyTest::testExistingError in modules/simpletest/tests/file.test
Test that copying over an existing file fails when FILE_EXISTS_ERROR is specified.
FileCopyTest::testExistingRename in modules/simpletest/tests/file.test
Test renaming when copying over a file that already exists.
FileCopyTest::testExistingReplace in modules/simpletest/tests/file.test
Test replacement when copying over a file that already exists.
FileCopyTest::testNormal in modules/simpletest/tests/file.test
Test file copying in the normal, base case.
FileTokenReplaceTestCase::testFileTokenReplacement in modules/file/tests/file.test
Creates a file, then tests the tokens generated from it.

... See full list


includes/file.inc, line 803
API for handling file uploads and server file management.


function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
  if (!file_valid_uri($destination)) {
    if (($realpath = drupal_realpath($source->uri)) !== FALSE) {
      watchdog('file', 'File %file (%realpath) could not be copied, because the destination %destination is invalid. This is often caused by improper use of file_copy() or a missing stream wrapper.', array('%file' => $source->uri, '%realpath' => $realpath, '%destination' => $destination));
    else {
      watchdog('file', 'File %file could not be copied, because the destination %destination is invalid. This is often caused by improper use of file_copy() or a missing stream wrapper.', array('%file' => $source->uri, '%destination' => $destination));
    drupal_set_message(t('The specified file %file could not be copied, because the destination is invalid. More information is available in the system log.', array('%file' => $source->uri)), 'error');
    return FALSE;

  if ($uri = file_unmanaged_copy($source->uri, $destination, $replace)) {
    $file = clone $source;
    $file->fid = NULL;
    $file->uri = $uri;
    $file->filename = drupal_basename($uri);
    // If we are replacing an existing file re-use its database record.
    if ($replace == FILE_EXISTS_REPLACE) {
      $existing_files = file_load_multiple(array(), array('uri' => $uri));
      if (count($existing_files)) {
        $existing = reset($existing_files);
        $file->fid = $existing->fid;
        $file->filename = $existing->filename;
    // If we are renaming around an existing file (rather than a directory),
    // use its basename for the filename.
    elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) {
      $file->filename = drupal_basename($destination);

    $file = file_save($file);

    // Inform modules that the file has been copied.
    module_invoke_all('file_copy', $file, $source);

    return $file;
  return FALSE;


DuaelFr’s picture

This function does not allow its $source file to be located on a distant server.

Here is how to deal with it :

$data = drupal_http_request($url);
$file = file_save_data($data->data, $destination);

Thanks marcingy for the tip.

netgenius.co.uk’s picture

MacMladen’s picture

Otherwise, all files are copied to the name of supposed directory.

It would be better if file_copy would create destination if it does not exist or, at least, drop out with error.

So it is up to coder to check if directory exists before calling.

acbramley’s picture

You can use the file_prepare_directory() function to achieve that by calling it before file_copy()

ventzie’s picture

It is called inside file_unmanaged_copy
if (file_prepare_directory($destination)) {
// The destination is already a directory, so append the source basename.
$destination = file_stream_wrapper_uri_normalize($destination . '/' . drupal_basename($source));

ventzie’s picture

Whats the point of the default parameter $destination ?!!! If I call the function like that:
file_copy($source), it will return FALSE, and will log an error, because file_valid_uri(NULL) will return FALSE ???

cbiggins’s picture

ventzie, regardless of what happens, the function will call file_unmanaged_copy() which, in its documentation, says;

$destination: A URI containing the destination that $source should be copied to. The URI may be a bare filepath (without a scheme). If this value is omitted, Drupal's default files scheme will be used, usually "public://".

In other words, it copies into the default public files directory.

ventzie’s picture

No, it will not call it:
if (!file_valid_uri($destination))
this is the first line. It will never go to file_unmanaged_copy() and it will return FALSE.
Don't you see what the function does?