Я пытаюсь сохранить MultipartFile (загруженный в контроллер Spring MVC) на сервер Linux в сети (требуется аутентификация). Я попробовал smb с помощью jcifs, но производительность очень низкая.
Может ли кто-нибудь указать мне альтернативный способ сделать это? Я искал везде в течение 2 дней и не смог найти решение, которое работает.
Сервер приложений работает под управлением Linux.
Обновлено: это код, который я использую. Код работает, но работает очень, очень плохо
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileInputStream;
import jcifs.smb.SmbFileOutputStream;
@Component
public class FileUploadUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(FileUploadUtil.class);
@Value("${dr.fileuser}")
private String user;
@Value("${dr.filepwd}")
private String pass;
@Value("${dr.sharePath}")
private String drSharePath;
@Value("${dr.fileLocation}")
private String drFileLocation;
@Value("${dr.serverName}")
private String drServerName;
/**
* @param uploadedFile
* @param filename
* @param discId: this is a generated id, used to associate these files with a database record.
* @return
* @throws WW_Exception
* @throws IOException
*/
public String writeFileToServer(MultipartFile uploadedFile, String filename, Integer discId) throws WW_Exception, IOException {
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("",user, pass);
String destDir = StringUtils.cleanPath("smb://" + drServerName + drSharePath + drFileLocation + discId + "/");
LOGGER.info("FILE Destination DIR: {}", destDir);
try{
//create directory structure
SmbFile sfileDir = new SmbFile(destDir, auth);
if (!sfileDir.exists()) {
sfileDir.mkdirs();
}
String destFilePath = StringUtils.cleanPath(destDir + filename);
LOGGER.info("FILE Destination PATH: {}", destFilePath);
SmbFile sfile = new SmbFile(destFilePath, auth);
try (SmbFileOutputStream fos = new SmbFileOutputStream(sfile)){
fos.write(uploadedFile.getBytes());
}
return destFilePath.replace("smb:", "");
} catch(Exception e){
throw e;
}
}
/**
* @param drId: this is a generated id, used to associate these files with a database record.
* @param origFilePath
* @return
* @throws IOException
*/
public String copyFileFromServer(Integer drId, String origFilePath) throws IOException {
LOGGER.info("FILE to get: {}",origFilePath);
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("",user, pass);
String[] imagePathInfo = origFilePath.split("/");
String destFilePath = StringUtils.cleanPath("/weblogs/tmp/" + drId + "/" + imagePathInfo[imagePathInfo.length-1]);
File destDir = new File(StringUtils.cleanPath("/weblogs/tmp/" + drId + "/"));
if (!destDir.exists()) {
destDir.mkdirs();
}
SmbFile origFile = new SmbFile(origFilePath,auth);
try(InputStream in = new SmbFileInputStream(origFile)) {
Files.copy(in, Paths.get(destFilePath), StandardCopyOption.REPLACE_EXISTING);
}
return destFilePath;
}
}
Кроме того, вы пытаетесь связать этот загруженный контент с объектами Spring Data или чем-то подобным или просто сохраняете файл по известному пути на сервере для последующего извлечения?
Привет @PaulWarren, я могу получить root-доступ к серверу. Я просто пытаюсь сохранить файл для последующего поиска. Прямо сейчас использование jcifs занимает около 8 секунд для хранения файла размером 8 МБ, что слишком долго. Спасибо!




Пожалуйста, проверьте, где вы пытаетесь сохранить MultipartFile, этот файл имеет разрешение на чтение и запись на Linux-машине.
Привет @iragond, я только что обновил пост. Код не дает сбоев, он просто работает очень плохо.
@ Хорхе,
Основываясь на ваших комментариях выше, поскольку у вас есть root-доступ к серверу Linux, все, что вам нужно сделать, это обычное старое монтирование ядра. Вам нужно будет убедиться, что вы установили клиент cifs (при условии, что Ubuntu):
$ sudo apt install -y cifs-utils
Затем вы сможете смонтировать свою долю с помощью чего-то вроде:
$ SMB_USERNAME=<your username>
$ SMB_PASSWORD=<your password>
$ SMB_SERVER = "//<your host>/<your share>"
$ sudo mount -t cifs -o username=${SMB_USERNAME},password=${SMB_PASSWORD} \
"${SMB_SERVER}" /mnt
Тогда вариант 1 будет состоять в том, чтобы изменить ваш код для чтения и записи файлов в смонтированный каталог; то есть /mnt в этом случае.
Или, вариант 2, будет использовать проект сообщества под названием Весенний контент. Это обеспечивает абстракцию над хранилищем для обработки ресурсов и может внедрить код контроллера и службы для вас, чтобы вам не нужно было писать его самостоятельно. Одним из поддерживаемых модулей хранения является модуль хранилища файловой системы, и вы должны настроить его для чтения и записи файлов в ваш локальный каталог /mnt, который на самом деле является вашим удаленным общим ресурсом.
Итак, если вы добавили Spring Content в свой проект, вы можете удалить весь код своего контроллера и не беспокоиться о деталях реализации. Кроме того, поскольку Spring Content является абстракцией, в будущем вы также можете перейти на любой другой носитель данных, поддерживаемый Spring Content; С3 например. Добавление будет выглядеть примерно так:
pom.xml (assuming maven. Spring boot starters also available)
<!-- Java API -->
<!-- just change this depdendency if you want to store somewhere else -->
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-fs</artifactId>
<version>0.7.0</version>
</dependency>
<!-- REST API -->
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest</artifactId>
<version>0.7.0</version>
</dependency>
StoreConfig.java
@Configuration
@EnableFilesystemStores
@Import(RestConfiguration.class)
public class StoreConfig {
@Bean
FileSystemResourceLoader fileSystemResourceLoader() throws IOException {
return new FileSystemResourceLoader(new File("/mnt").getAbsolutePath());
}
}
FileStore.java
@StoreRestResource(path = "files")
public interface FileStore extends Store<String> {
}
Вот и все. FileStore — это, по сути, универсальный Spring ResourceLoader. Зависимость spring-content-fs заставит Spring Content внедрить реализацию на основе файловой системы, поэтому вам не нужно беспокоиться о ее реализации самостоятельно. Более того, зависимость spring-content-rest приведет к тому, что Spring Content также внедрит реализацию, если @Controller перенаправляет HTTP-запросы в метод FileStore.
Таким образом, теперь у вас будет полнофункциональная (POST, PUT, GET, DELETE) файловая служба на основе REST в /files, которая будет использовать ваш FileStore для извлечения (и хранения) файлов в /mnt; то есть на вашем удаленном SMB-сервере.
Так:
GET /files/some/file.csv
будет скачивать file.csv из /path/to/your/files/some/.
А также...
curl --upload-file some-other-file.csv /files/some-other-file.csv
загрузит some-other-file.csv и сохранит его в /mnt/ на вашем сервере.
А также:
curl /files/some-other-file.csv
получит его снова.
ХТН
Введенный контроллер также поддерживает потоковое видео, если это полезно.
спасибо, @PaulWarren... Я попробую и опубликую результаты.
есть ли способ указать подкаталог во время загрузки файла с помощью Spring Content? Некоторые из наших загрузок основаны на идентификаторе строки и должны поместить все связанные файлы в один и тот же подкаталог.
@ Хорхе, да. если вы загрузили в /files/a/b/some-other-file.csv, то он будет сохранен в ``/mnt/a/b/some-other-file.csv`
Привет @Jorge, у тебя есть root-доступ на рассматриваемом сервере?