I have a class dedicated to encode and decode files. The encode method receives in its signature a string corresponding to the file path to encode. The encodeFileToBase64Binary is returning a simple String that I use to build a JSON object.
My objective would be to get the file name to the decode folder in order to create a new file. Those two methods are called at distinct moments.
public class FileConverterBase64 {
private ApplicationProperties properties = new ApplicationProperties();
private String toSignFilePath;
public String encodeFileToBase64Binary(String filePath) throws IOException {
this.toSignFilePath = filePath;
byte[] fileContent = FileUtils.readFileToByteArray(new File(filePath));
return Base64.getEncoder().encodeToString(fileContent);
}
public void stringDecodeFileToPdf(String encodedFile) throws IOException {
System.out.println("toSignFilePath : " + toSignFilePath);
// deposit repository
String folder = properties.getProperty("document.signed");
LocalDateTime now = LocalDateTime.now();
String outputFileName = folder + "\\" + now + "" + toSignFilePath + "-signed";
byte[] decodedBytes = Base64.getDecoder().decode(encodedFile);
FileUtils.writeByteArrayToFile(new File(outputFileName), decodedBytes);
}
}
Related
I am going to convert MultipartFile to File and upload it to S3 bucket.
However, when running the test, an error occurs in the process of converting MultipartFile to File.
ERROR : java.nio.file.AccessDeniedException: D:\workspace_intellij_forKiri\Kiri\server\kiri\temp\8b28a2f2-7276-4036
multipartFile.transferTo(file);
Please advise if there is anything I am missing.
The spring boot version is 2.7.7 version.
Test code
#WithAccount("creamyyyy")
#DisplayName("image save test")
#Test
public void createImageTest() throws Exception {
//given
String filename = "files";
String contentType = "png";
MockMultipartFile image1 = new MockMultipartFile(
filename,
filename + "." + contentType,
"image/png",
filename.getBytes());
//when
//then
this.mockMvc.perform( //== ERROR!!!
MockMvcRequestBuilders
.multipart("/api/posts/image")
.file(image1)
.contentType(MediaType.MULTIPART_FORM_DATA)
.characterEncoding("UTF-8")
)
.andDo(print())
.andExpect(status().isOk());
}
ImageService Code
// FileSave
public List<ImageResDto> addFile(List<MultipartFile> multipartFiles) throws IOException {
List<ImageResDto> imageResDtoList = new ArrayList<>();
/**
* <ImageResDto>
* private Long image_id;
* private String imgUrl;
*/
String absolutePath = new File("").getAbsolutePath() + File.separator + "temp";
for (MultipartFile multipartFile : multipartFiles) {
String contentType = multipartFile.getContentType();
if(ObjectUtils.isEmpty(contentType)) {
throw new RuntimeException("FILE TYPE NOT FOUND");
} else if(!verifyContentType(contentType)){
throw new RuntimeException("FILE TYPE NOT FOUND");
}
}
for (MultipartFile multipartFile : multipartFiles) {
String filename = UUID.randomUUID() + multipartFile.getOriginalFilename();
// save in local
String fullFilePath = absolutePath + File.separator + filename;
System.out.println("fullFilePath = " + fullFilePath);
File file = new File(fullFilePath);
if(!file.exists()) { file.mkdirs(); }
multipartFile.transferTo(file); // ERROR ... OTL
file.createNewFile();
// S3 upload
amazonS3.putObject(
new PutObjectRequest(bucket, filename, file)
.withCannedAcl(CannedAccessControlList.PublicRead)
);
String imgUrl = amazonS3.getUrl(bucket, filename).toString();
Image newImage = Image.builder()
.filename(filename)
.filepath(filename)
.imgUrl(imgUrl)
.build();
imageRepository.save(newImage);
ImageResDto imageResDto = ImageResDto.of(newImage);
imageResDtoList.add(imageResDto);
file.delete(); // local file delete
}
return imageResDtoList;
}
ImageController Code
#PostMapping(value = "/api/posts/image", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity createImage(#RequestPart(value = "files") List<MultipartFile> multipartFiles) throws IOException {
System.out.println("ImageController Runnnn");
// get member
PrincipalDetails principalDetails = (PrincipalDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Member member = principalDetails.getMember();
List<ImageResDto> imageResDtoList = imageService.addFile(multipartFiles);
return new ResponseEntity(imageResDtoList, HttpStatus.CREATED);
}
I tried to specify a separate route using Path, but I failed.
// Error ..java.nio.file.AccessDeniedException => Path
// multipartFile -> File
Path path = Paths.get(fullFilePath).toAbsolutePath();
multipartFile.transferTo(path.toFile());
Files.createFile(path);
What is incomprehensible is that when tested using PostMan, the file is normally uploaded to the S3 bucket.
Please tell me if I applied anything wrong.
I am trying to create a file system integration for a Spring Boot web application.
I have a currently working solution that stores the file content in file system and file name and location in a database.
Contents on disk:
I am trying to find a way to separate saved files into folders. The root folder should be "file-system" and files should be separated into folders in a way that one folder contains no more than 500 files.
What is the correct way to do this?
Is there a directory tree manager that is built in to Spring or Java that I can use?
My current solution is below.
DBFile:
#Entity
public class DBFile {
#Id
private Long id;
#Column(name = "name")
private String name;
#Column(name = "location")
private String location;
}
FileSystemRepository:
#Repository
public class FileSystemRepository {
public static final String RESOURCES_ROOT_DIR = "/file-system/";
public String save(byte[] content, String fileName, String contentType) throws IOException {
Path path = getPath(fileName, contentType);
Files.createDirectories(path.getParent());
Files.write(path, content);
return path.toAbsolutePath().toString();
}
public FileSystemResource findInFileSystem(String location) {
return new FileSystemResource(Paths.get(location));
}
public void deleteInFileSystem(String location) throws IOException {
Files.delete(Paths.get(location));
}
private Path getPath(String fileName, String contentType) {
Path path = FileSystems.getDefault().getPath("").toAbsolutePath();
String subDirectory;
if (contentType.startsWith("image/")) {
subDirectory = "images/";
} else {
subDirectory = "files/";
}
return Paths.get(path + RESOURCES_ROOT_DIR + subDirectory + new Date().getTime() + "-" + fileName);
}
}
FileSystemService:
#Service
public class FileSystemService {
private final FileSystemRepository fileSystemRepository;
private final DBFileRepository dbFileRepository;
public Long save(byte[] bytes, String imageName, String contentType) {
try {
String location = fileSystemRepository.save(bytes, imageName, contentType);
return dbFileRepository.save(new DBFile(imageName, location))
.getId();
} catch (IOException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
}
}
public FileSystemResource find(Long id) {
DBFile image = dbFileRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
return fileSystemRepository.findInFileSystem(image.getLocation());
}
public void delete(Long id) {
DBFile image = dbFileRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
try {
fileSystemRepository.deleteInFileSystem(image.getLocation());
} catch (IOException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
}
dbFileRepository.delete(image);
}
}
I was struggling with this issue for quite a while, but now I found a possible solution that a nice colleague of mine sent to me.
A possible solution is to create a hashed directory structure.
More information and also an example that I used can be found here:
https://medium.com/eonian-technologies/file-name-hashing-creating-a-hashed-directory-structure-eabb03aa4091
My final solution to create a path from file name:
private static Path getPath(String fileName) {
Path root = FileSystems.getDefault().getPath("").toAbsolutePath();
int hash = fileName.hashCode();
int mask = 255;
int firstDir = hash & mask;
int secondDir = (hash >> 8) & mask;
String path = root +
File.separator +
RESOURCES_ROOT_DIR +
File.separator +
String.format("%03d", firstDir) +
File.separator +
String.format("%03d", secondDir) +
File.separator +
getDatedFileName(fileName);
return Paths.get(path);
}
private static String getDatedFileName(String fileName) {
LocalDateTime today = LocalDateTime.now();
return String.format("%s-%s-%s_%s_%s_%s-%s",
today.getYear(),
today.getMonthValue(),
today.getDayOfMonth(),
today.getHour(),
today.getMinute(),
today.getSecond(),
fileName);
}
I am working on an application that allows users to upload files. I want to keep the uploaded files organized into pre-created folders named for the group that the user belongs to. I can't seem to find a way to make the path editable so that I can pass the group's name into the method as a parameter and have the file stored in that directory.
Here's my latest attempt that results in a "Failed to store file file] with root cause" exception.
#Service
public class StorageServiceImpl implements StorageService {
#Value("${upload.path}")
private Path path;
public void uploadFile(MultipartFile file,String contentName, String groupName){
//make so that files are stored in path/groupname
this.path = Paths.get(this.path.toString() +"/"+groupName +"/");
String filename = contentName+"-"+StringUtils.cleanPath(file.getOriginalFilename());
System.out.println("\n\n\n\n\n"+filename + "\n\n\n");
try {
if (file.isEmpty()) {
throw new StorageException("Failed to store empty file");
}
if (filename.contains("..")) {
// This is a security check
throw new StorageException(
"Cannot store file with relative path outside current directory "
+ filename);
}
try (InputStream inputStream = file.getInputStream()) {
System.out.println("\n\n\n\n\n"+this.path.resolve(filename) + "\n\n\n");
Files.copy(inputStream, this.path.resolve(filename), StandardCopyOption.REPLACE_EXISTING);
}
}catch (IOException e) {
String msg = String.format("Failed to store file %s", file.getName());
throw new StorageException(msg, e);
}
}
}
Note: If the directory of the groupName is created before this method runs (as I intend to have it created when the group is created) then the method attempts to store the file in another directory of the same name inside that directory such as:
backend/src/main/webapp/WEB-INF/TestGroup/TestGroup/test.jpg
See how TestGroup shows up twice
Instead of doing this, here you are trying to inject value to a varible of type Path.
#Value("${upload.path}")
private Path path;
how about reading the value to a String variable and storing it as a File and then using it to upload like below,
#Value("${upload.path}")
private String path;
And then using it
public void uploadFile( MultipartFile file, String contentName, String groupName )
{
String filename = contentName + "-" + StringUtils.cleanPath( file.getOriginalFilename() );
// use a File object here
File uploadFilePath = Paths.get( new File( path ).getPath() + "/" + groupName + "/" + filename ).toFile();
try
{
try( InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream( uploadFilePath ) )
{
// Leverage the support of Spring's FileCopyUtils Here
FileCopyUtils.copy( in, out );
}
catch( IOException ex )
{
throw new RuntimeException( ex );
}
}
catch( IOException e )
{
String msg = String.format( "Failed to store file %s", file.getName() );
throw new StorageException( msg, e );
}
}
I got it working by using the absolutePath method from the private Path path to generate the string.
public void uploadFile(MultipartFile file,String contentName, String groupName){
//make so that files are stored in path/groupname
String filename = contentName+"-"+StringUtils.cleanPath(file.getOriginalFilename());
File uploadFilePath = Paths.get(this.path.toAbsolutePath() + "/" + groupName+"/" + filename).toFile();
try {
if (file.isEmpty()) {
throw new StorageException("Failed to store empty file");
}
if (filename.contains("..")) {
// This is a security check
throw new StorageException(
"Cannot store file with relative path outside current directory "
+ filename);
}
try (InputStream inputStream = file.getInputStream();OutputStream out = new FileOutputStream(uploadFilePath);) {
FileCopyUtils.copy(inputStream,out);
}
}catch (IOException e) {
String msg = String.format("Failed to store file %s", file.getName());
throw new StorageException(msg, e);
}
}
I want to write a String to a file, which the user chooses from . I'm unable to do it because I need the fileName and fileLocation to write my String to the file. But the request.getParamater("") gives me just the fileName. I know that it won't return the fileLocation because of security issues. Then, how do I write my String from the file chosen on my jsp. Please advise.
You cannot write to that file directly.
Short answer : Make a copy file on server.
Long answer:
1) Get the file.
2) Save it on server.
3) Append to that file. Do not overwrite.
4) Again send back the file to user with response.
Do some steps
Get file name using getFileName() method. Store it on server side
if some one wants to Save Same file name again then you append Some Date after file Name. so you can easily get all the files without any code changes.
After write String into file close the file and flush .
See i have upload the file form Front End and Store it on Some local system . when you try to upload Same file again then it append Date with File name
public class uploadFile {
private File myFile;
private String myFileContentType;
private String myFileFileName;
private String destPath;
public String upload() throws IOException {
try {
destPath = System.getProperty("user.home") + System.getProperty("file.separator") + "File-Uploads";
File destFile = new File(destPath, myFileFileName);
File file1 = new File(destFile.toString());
boolean b = false;
Date date = new Date();
if (!(file1.exists())) {
b = file1.createNewFile();
}
if (b) {
FileUtils.copyFile(myFile, destFile);
} else {
String fileContent = "";
File f = new File(file1.toString());
FileInputStream inp = new FileInputStream(f);
byte[] bf = new byte[(int) f.length()];
inp.read(bf);
fileContent = new String(bf, "UTF-8");
System.out.println("file===>" + fileContent);
String filename = destFile.toString() + date;
FileWriter fw = new FileWriter(filename, true);
fw.write(fileContent);
fw.close();
FileUtils.copyFile(myFile, destFile);
}
return SUCCESS;
} catch (IOException e) {
return SUCCESS;
}
}
public File getMyFile() {
return myFile;
}
public void setMyFile(File myFile) {
this.myFile = myFile;
}
public String getMyFileContentType() {
return myFileContentType;
}
public void setMyFileContentType(String myFileContentType) {
this.myFileContentType = myFileContentType;
}
public String getMyFileFileName() {
return myFileFileName;
}
public void setMyFileFileName(String myFileFileName) {
this.myFileFileName = myFileFileName;
}
}
My code is:
public class RemotePlay {
static final String USER_NAME = "bwisniewski";
static final String PASSWORD = "xxx";
static final String NETWORK_FOLDER = "smb://192.168.1.141/ADMIN$/";
public static void main(String[] args) throws IOException, InterruptedException {
// TODO Auto-generated method stub
String fileContent = "This is a test File";
new RemotePlay().copyFiles(fileContent, "testFile1.txt");
}
public boolean copyFiles(String fileContent, String fileName) {
boolean successful = false;
try{
String user = USER_NAME + ":" + PASSWORD;
System.out.println("User: "+user);
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(user);
String path = NETWORK_FOLDER + fileName;
System.out.println("Path: "+path);
SmbFile sFile = new SmbFile(path, auth);
SmbFileOutputStream sfos = new SmbFileOutputStream(sFile);
sfos.write(fileContent.getBytes());
successful = true;
System.out.println("Successful "+successful);
}
catch(Exception e) {
successful = false;
e.printStackTrace();
}
return successful;
}}
How can I change it to send exe file to ADMIN$ share. I prefer to use this method because I have to authenticate to remote pc. If you got better ideas to copy file to ADMIN$ share I am looking forward to hear about it.
Thanks.
sfos.write(fileContent.getBytes());
if your data is text, then why not to use PrintWriter to write down your file
public static void main(String [] args) throws Exception { // temporary
File fileOne = new File("testfile1.txt");
PrintWriter writer = new PrintWriter(fileOne);
// write down data
writer.println("This is a test File");
// free resources
writer.flush();
writer.close();
}
and about the extension, you can use any extension you want while creating the file, it will still hold the data, and can be opened if you renamed it to the correct extension on the hard drive
if you named your file testfile.exe it will still hold your data, but when you double click it it wont work until you rename it to testfile.txt (or it will work if the extension is compatible with the data in the file)