File.Move não funciona - o arquivo já existe


86

Eu tenho uma pasta:

c: \ teste

Estou tentando este código:

File.Move(@"c:\test\SomeFile.txt", @"c:\test\Test");

Eu recebo uma exceção:

O arquivo já existe

O diretório de saída definitivamente existe e o arquivo de entrada está lá.


2
Se o arquivo de entrada já está no diretório de saída, então o arquivo já existe, explicando a exceção. Você precisa indicar que deseja que o arquivo original seja sobrescrito pelo novo.
Cody Gray

9
Parece que o erro está dizendo exatamente o que está errado.
Josh

@Josh Não. Parece que o Windows está tendo um comportamento de sistema de arquivos não POSIX, o que torna impossível descobrir um padrão / rotina simples de atualização de arquivo transacional portátil.
binki

@binki POSIX é irrelevante (você está se referindo a operações atômicas ?), o NTFS oferece suporte a operações transacionais reais, como rollback-and-get-the-original-file-content-back. Como outros respondeu: Win32 faz permitir movimento com substituir. É o File.Move do .NET que não fornece a funcionalidade. Você pode obter o Move com substituição e operações transacionais com bibliotecas como AlphaFS
Panagiotis Kanavos

2
@binki em qualquer caso, o comportamento é bem definido em diferentes sistemas de arquivos , não importa o que as discussões do fórum digam. A razão de File.Move não chamar os métodos Ex ou Transacted é que o FAT, que não pode ser ignorado porque ainda é usado por cartões de memória, não é atômico e não se comporta da mesma forma. Renomeações não são operações de metadados e requerem movimentação real de dados. E esqueça as transações e a cópia na gravação. Não é uma boa decisão imho
Panagiotis Kanavos

Respostas:


62

Você precisa movê-lo para outro arquivo (em vez de uma pasta), isso também pode ser usado para renomear.

Mover:

File.Move(@"c:\test\SomeFile.txt", @"c:\test\Test\SomeFile.txt");

Renomear:

File.Move(@"c:\test\SomeFile.txt", @"c:\test\SomeFile2.txt");

O motivo pelo qual diz "Arquivo já existe" em seu exemplo é porque C:\test\Testtenta criar um arquivo Testsem uma extensão, mas não consegue porque já existe uma pasta com o mesmo nome.


138

O que você precisa é:

if (!File.Exists(@"c:\test\Test\SomeFile.txt")) {
    File.Move(@"c:\test\SomeFile.txt", @"c:\test\Test\SomeFile.txt");
}

ou

if (File.Exists(@"c:\test\Test\SomeFile.txt")) {
    File.Delete(@"c:\test\Test\SomeFile.txt");
}
File.Move(@"c:\test\SomeFile.txt", @"c:\test\Test\SomeFile.txt");

Isso irá:

  • Se o arquivo não existir no local de destino, mova o arquivo com êxito ou;
  • Se o arquivo existir no local de destino, exclua-o e mova o arquivo.

Edit: Devo esclarecer minha resposta, embora seja a mais votada! O segundo parâmetro de File.Move deve ser o arquivo de destino - não uma pasta. Você está especificando o segundo parâmetro como a pasta de destino, não o nome do arquivo de destino - que é o que File.Move requer. Portanto, seu segundo parâmetro deve ser c:\test\Test\SomeFile.txt.


Certamente não precisa verificar se o arquivo não está lá, porque ele está verificando e o arquivo não está lá. A exceção é causada por não anexar o nome do arquivo à pasta de destino ao tentar movê-lo para outra pasta.
Hadi Eskandari

3
Se o seu aplicativo for multiencadeado (ou houver outros processos trabalhando em seus arquivos), você ainda poderá obter a mesma exceção, mesmo usando o código "if (Exists) Delete". Como ainda há um espaço de tempo em que outro processo / thread pode colocar um arquivo de volta após a exclusão, então você faz sua movimentação e obtém a exceção de qualquer maneira. Vale a pena ter em mente :-)
bytedev

11
Esta resposta ainda é válida para a maioria das pessoas que pesquisam no Google depois de tentar substituir um arquivo existente. A maioria das pessoas nessa situação não tem um problema de sintaxe / tipo-o como o OP.
WEFX de

1
@ v.oddou curiosamente, se o arquivo não existe, File.Delete realmente funciona corretamente e não faz nada. Se, em vez disso, qualquer um dos diretórios no caminho não existir, você obterá uma DirectoryNotFoundException.
Brandon Barkley de

2
@JirkaHanika você pode alterar if (File.Exists) para while (File.Exists).
Brandon Barkley de

38

Pessoalmente, prefiro esse método. Isso substituirá o arquivo no destino, removerá o arquivo de origem e também impedirá a remoção do arquivo de origem quando a cópia falhar.

string source = @"c:\test\SomeFile.txt";
string destination = @"c:\test\test\SomeFile.txt";

try
{
    File.Copy(source, destination, true);
    File.Delete(source);
}
catch
{
    //some error handling
}

4
Isso é bom para arquivos pequenos (e não há necessidade de movimentação atômica), mas para arquivos grandes, ou casos em que você precisa ter certeza de que não ficará com duplicatas, é problemático.
Rio Satya de

Por que você prefere File.Copy , File.Deletemais File.Move?
John Pietrar,

6
File.Move não tem uma opção de substituição.
Mitchell,

1
Dependendo do seu caso de uso, isso pode causar problemas. "Mover" é um evento real em um observador de sistema de arquivos. Algo listando os eventos do sistema de arquivos obterá um evento de exclusão e criação em vez de um evento de movimentação. Isso também mudará o ID do sistema de arquivos subjacente.
Andrew Rondeau

1
Isso não terá um desempenho muito menor para arquivos grandes? Se a origem e o destino estiverem no mesmo volume físico, você está criando uma segunda cópia sem motivo e, em seguida, excluindo o original, enquanto File.Move () evitará trabalho extra se a origem e o destino estiverem no mesmo volume.
Brad Westness

18

Você pode fazer um P / Invoke para MoveFileEx()- passar 11 para flags( MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)

[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
static extern bool MoveFileEx(string existingFileName, string newFileName, int flags);

Ou você pode apenas ligar

Microsoft.VisualBasic.FileIO.FileSystem.MoveFile(existingFileName, newFileName, true);

após adicionar Microsoft.VisualBasic como uma referência.


Totalmente bem se o aplicativo estiver rodando apenas no Windows. Esta é provavelmente uma boa resposta para a maioria das pessoas que desejam experimentar o P / Invoke.
Todd

9

Se o arquivo realmente existe e você deseja substituí-lo, use o código abaixo:

string file = "c:\test\SomeFile.txt"
string moveTo = "c:\test\test\SomeFile.txt"

if (File.Exists(moveTo))
{
    File.Delete(moveTo);
}

File.Move(file, moveTo);

4

De acordo com os documentos para File.Move, não há parâmetro "sobrescrever se existir". Você tentou especificar a pasta de destino , mas deve fornecer a especificação completa do arquivo.

Lendo os documentos novamente ("fornecendo a opção de especificar um novo nome de arquivo"), eu acho , adicionar uma barra invertida às especificações da pasta de destino pode funcionar.


E os documentos mencionam. Observe que se você tentar substituir um arquivo movendo um arquivo com o mesmo nome para esse diretório, uma IOException é lançada. Para isso, ligue Move(String, String, Boolean). mas isso parece ser um erro?
Kevin Scharnhorst

@KevinScharnhorst Esta resposta foi 2011. A documentação agora inclui suporte para .Net Core 3.0 para Mover com Substituição.
Todd

4

1) Com C # no .Net Core 3.0 e além, agora há um terceiro parâmetro booleano:

consulte https://docs.microsoft.com/en-us/dotnet/api/system.io.file.move?view=netcore-3.1

In .NET Core 3.0 and later versions, you can call Move(String, String, Boolean) setting the parameter overwrite to true, which will replace the file if it exists.

2) Para todas as outras versões do .Net, https://stackoverflow.com/a/42224803/887092 é a melhor resposta. Copie com sobrescrever e exclua o arquivo de origem. Isso é melhor porque torna uma operação atômica. (Eu tentei atualizar o MS Docs com isso)



1

Se você não tem a opção de excluir o arquivo já existente no novo local, mas ainda precisa mover e excluir do local original, este truque de renomeação pode funcionar:

string newFileLocation = @"c:\test\Test\SomeFile.txt";

while (File.Exists(newFileLocation)) {
    newFileLocation = newFileLocation.Split('.')[0] + "_copy." + newFileLocation.Split('.')[1];
}
File.Move(@"c:\test\SomeFile.txt", newFileLocation);

Isso pressupõe o único '.' no nome do arquivo está antes da extensão. Ele divide o arquivo em dois antes da extensão e anexa "_copy". entre. Isso permite que você mova o arquivo, mas cria uma cópia se o arquivo já existe ou uma cópia da cópia já existe, ou uma cópia da cópia da cópia existe ...;)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.