The question is rather simple. I am using the aspose library to convert a pdf file to excel. The excel file is subsequently written to the database and this generated excel file is not needed in the future.
My method:
public void main(MultipartFile file) throws IOException {
InputStream inputStream = file.getInputStream();
Document document = new Document(inputStream);
ExcelSaveOptions options = new ExcelSaveOptions();
options.setFormat(ExcelSaveOptions.ExcelFormat.XLSX);
document.save("newExcelFile.xlsx", options);
}
In this method, the file is saved to the root folder of the project (if it is running locally). How can I not store this file, but make it temporary? My question is that this project is located on the server, and I would not like to create directories specifically for this file.
The Document.save() method has an overload for saving to an OutputStream (See here for the API reference).
Given that you can store the result to anything that implements an OutputStream, you can provide any implementation that you want - one useful option might be to use ByteArrayOutputStream to store the result in memory, or possibly - just use Files.createTempFile() and create a FileOutputStream for that.
For example, your code may be rewritten thus:
public byte[] convertToExcel(MultipartFile file) throws IOException {
InputStream inputStream = file.getInputStream();
Document document = new Document(inputStream);
ExcelSaveOptions options = new ExcelSaveOptions();
options.setFormat(ExcelSaveOptions.ExcelFormat.XLSX);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos, options);
return baos.toByteArray();
}
Related
In Java, how do you split a binary file into multiple parts while only loading a small portion of the File into memory at one time?
So I have a file FullFile that is large. I need to upload it to cloud storage but it's so large that it often times out.
I can make this problem less likely if I split the file and upload in chunks.
So I need to split FullFile into files of chunk size MaxChunkSize.
List<File> fileSplit(File fullFile, int maxChunkSize)
File fileJoin(List<File> splitFiles)
Most code snippets around require the file to be text. But in my case the files are compressed binary.
What would be the best way to implement these methods?
Below is the full answer:
The maxChunkSize represents the size in bytes of a file chunk.
In the example below I read a 5mb zip file and split it into five 1MB chunks and later join them back using the fileJoin function.
The method stageLocally stages the files locally but you can modify it to work with any cloud storage. (Better to abstract this out so you can switch between multiple storage implementations)
You can tweak maxChunkSize based on the amount of data you want to store inmemory at a given time
The IOutils.copy() methods is from the commons library, here is the maven link. You can also use Files.copy() in liue of it. The Files.copy() methods comes from the java.nio package, so you don't have to add an external dependency to use it.
I have ommitted the exception handling for brevity.
public static void main(String[] args) throws IOException {
File input = new File(_5_MB_FILE_PATH);
File outPut = fileJoin(split(input, 1_024_000));
System.out.println(IOUtils.contentEquals(Files.newInputStream(input.toPath()), Files.newInputStream(outPut.toPath())));
}
public static List<File> split(File largeFile, int maxChunkSize) throws IOException {
InputStream in = Files.newInputStream(largeFile.toPath());
List<File> list = new ArrayList<>();
final byte[] buffer = new byte[maxChunkSize];
int dataRead = in.read(buffer);
while (dataRead > -1) {
list.add(stageLocally(buffer, dataRead));
dataRead = in.read(buffer);
}
return list;
}
private static File stageLocally(byte[] buffer, int length) throws IOException {
File outPutFile = File.createTempFile("temp-", "split", new File(TEMP_DIRECTORY));
FileOutputStream fos = new FileOutputStream(outPutFile);
fos.write(buffer, 0, length);
fos.close();
return outPutFile;
}
public static File fileJoin(List<File> list) throws IOException {
File outPutFile = File.createTempFile("temp-", "unsplit", new File(TEMP_DIRECTORY));
FileOutputStream fileOutputStream = new FileOutputStream(outPutFile);
for (File file : list) {
InputStream in = Files.newInputStream(file.toPath());
IOUtils.copy(in, fileOutputStream);
in.close();
}
fileOutputStream.close();
return outPutFile;
}
Let me know if this helps.
I am having 1 problem. I save SVG images in the database as binary. Now, I want to download it without converting to base64, is there any way. Thank you.
Basically, that would mean getting the BLOB object from the database.
I would follow this approach to show it in directly in the browser:
#RestController
public class ImageController {
#GetMapping(value = "/img-test", produces = "image/svg+xml")
public byte[] getImg() throws IOException
{
// retrieve your image from the DB
File imgFile = new File("C:\\Users\\USR\\Desktop\\img.svg");
InputStream inp = new DataInputStream(new FileInputStream(imgFile));
return inp.readAllBytes(); // This is a Java 9 specific convertion
}
}
With this approach, you do not change anything on the BLOB image. You take it and return it as is, an array with bytes. And you can directly show it in a browser or embed it somewhere in your HTML file.
The main thing here is the MIME type : image/svg+xml
If you are using an older version of Java, then check this question for the conversion of the InputStream object to a byte array.
And with this approach you can download the file:
#GetMapping("download-img")
public ResponseEntity downloadImg() throws IOException
{
// Get the file from the DB...
File imgFile = new File("C:\\Users\\USR\\Desktop\\img.svg");
InputStream inp = new DataInputStream(new FileInputStream(imgFile));
//Dynamically change the File Name here
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"img.svg\"")
.body(inp.readAllBytes());
}
my question might not be entirely related to Java but I'm currently seeking a method to combine several compressed (gzipped) textfiles without the requirement to recompress them manually. Lets say I have 4 files, all text that is compressed using gzip and want to compress these into one single *.gz file without de + recompressing them. My current method is to open an InputStream and parse the file linewise, storing in a GZIPoutputstream, which works but isn't very fast.... I could of course also call
zcat file1 file2 file3 | gzip -c > output_all_four.gz
This would work, too but isn't really fast either.
My idea would be to copy the inputstream and write it to outputstream directly without "parsing" the stream, as I don't need to manipulate anything actually. Is something like this possible?
Find below a simple solution in Java (it does the same as my cat ... example). Any kind of buffering the input/output has been omitted to keep the code slim.
public class ConcatFiles {
public static void main(String[] args) throws IOException {
// concatenate the single gzip files to one gzip file
try (InputStream isOne = new FileInputStream("file1.gz");
InputStream isTwo = new FileInputStream("file2.gz");
InputStream isThree = new FileInputStream("file3.gz");
SequenceInputStream sis = new SequenceInputStream(new SequenceInputStream(isOne, isTwo), isThree);
OutputStream bos = new FileOutputStream("output_all_three.gz")) {
byte[] buffer = new byte[8192];
int intsRead;
while ((intsRead = sis.read(buffer)) != -1) {
bos.write(buffer, 0, intsRead);
}
bos.flush();
}
// ungezip the single gzip file, the output contains the
// concatenated input of the single uncompressed files
try (GZIPInputStream gzipis = new GZIPInputStream(new FileInputStream("output_all_three.gz"));
OutputStream bos = new FileOutputStream("output_all_three")) {
byte[] buffer = new byte[8192];
int intsRead;
while ((intsRead = gzipis.read(buffer)) != -1) {
bos.write(buffer, 0, intsRead);
}
bos.flush();
}
}
}
The above method works if you just require to gzip many zipped files. In my case I had made a web servlet and my response was in 20-30 KBs. So I was sending the zipped response.
I tried to zip all my individual JS files on server start only and then add dynamic code runtime using the above method. I could print the entire response in my log file but chrome was able to unzip the first file only. Rest output was coming in bytes.
After research I found out that this is not possible with chrome and they have closed the bug also without solving it.
https://bugs.chromium.org/p/chromium/issues/detail?id=20884
I have an uncompressed binary file in res/raw that I was reading this way:
public byte[] file2Bytes (int rid) {
byte[] buffer = null;
try {
AssetFileDescriptor afd = res.openRawResourceFd(rid);
FileInputStream in = new FileInputStream(afd.getFileDescriptor());
int len = (int)afd.getLength();
buffer = new byte[len];
in.read(buffer, 0, len);
in.close();
} catch (Exception ex) {
Log.w(ACTNAME, "file2Bytes() fail\n"+ex.toString());
return null;
}
return buffer;
}
However, buffer did not contain what it was supposed to. The source file is 1024 essentially random bytes (a binary key). But buffer, when written out and examined, was not the same. Amongst unprintable bytes at beginning appeared "res/layout/main.xml" (the literal path) and then further down, part of the text content of another file from res/raw. O_O?
Exasperated after a while, I tried:
AssetFileDescriptor afd = res.openRawResourceFd(rid);
//FileInputStream in = new FileInputStream(afd.getFileDescriptor());
FileInputStream in = afd.createInputStream();
Presto, I got the content correctly -- this is easily reproducible.
So the relevant API docs read:
public FileDescriptor getFileDescriptor ()
Returns the FileDescriptor that can be used to read the data in the
file.
public FileInputStream createInputStream ()
Create and return a new auto-close input stream for this asset. This
will either return a full asset
AssetFileDescriptor.AutoCloseInputStream, or an underlying
ParcelFileDescriptor.AutoCloseInputStream depending on whether the the
object represents a complete file or sub-section of a file. You should
only call this once for a particular asset.
Why would a FileInputStream() constructed from getFileDescriptor() end up with garbage whereas createInputStream() gives proper access?
As per pskink's comment, the FileDescriptor returned by AssetFileDescriptor() is apparently not an fd that refers just to the file -- it perhaps refers to whatever bundle/parcel/conglomeration aapt has made of the resources.
AssetFileDescriptor afd = res.openRawResourceFd(rid);
FileInputStream in = new FileInputStream(afd.getFileDescriptor());
in.skip(afd.getStartOffset());
Turns out to be the equivalent of the FileInputStream in = afd.createInputStream() version.
I suppose there is a hint in the difference between "create" (something new) and "get" (something existing). :/
AssetFileDescriptor can be thought of as the entry point to the entire package's assets data.
I have run into the same issue and solved it finally.
If you want to manually create a stream from an AssetFileDescriptor, you have to skip n bytes to the requested resource. It is like you are paging thru all the available files in one big file.
Thanks to pskink! I had a look at the hex content of the jpg image I want to acquire, it starts with -1. The thing is, there are two jpg images. I did not know, so I arbitrarily skip 76L bytes. Got the first image!
I have tried creating a file, using the code below:
import java.io.File;
public class DeleteEvidence {
public static void main(String[] args) {
File evidence = new File("cookedBooks.txt");
However, the file cookedBooks.txt does not exist anywhere on my computer. I'm pretty new to this, so I'm having problems understanding other threads about similar problems.
You have successfully created an instance of the class File, which is very different from creating actual files in your hard drive.
Instances of the File class are used to refer to files on the disk. You can use them to many things, for instance:
check if files or directories exist;
create/delete/rename files or directories; and
open "streams" to write data into the files.
To create a file in your hard disk and write some data to it, you could use, for instance, FileOutputStream.
public class AnExample {
public static void main(String... args) throws Throwable {
final File file = new File("file.dat");
try (FileOutputStream fos = new FileOutputStream(file);
DataOutputStream out = new DataOutputStream(fos)) {
out.writeInt(42);
}
}
}
Here, fos in an instance of FileOutputStream, which is an OutputStream that writes all bytes written to it to an underlying file on disk.
Then, I create an instance of DataOutputStream around that FileOutputStream: this way, we can write more complex data types than bytes and byte arrays (which is your only possibility using the FileOutputStream directly).
Finally, four bytes of data are written to the file: the four bytes representing the integer 42. Note that, if you open this file on a text editor, you will see garbage, since the code above did not write the characters '4' and '2'.
Another possibility would have been to use an OutputStreamWriter, which would give you an instance of Writer that can be used to write text (non-binary) files:
public class AnExample {
public static void main(String... args) throws Throwable {
final File file = new File("file.txt");
try (FileOutputStream fos = new FileOutputStream(file);
OutputStreamWriter out = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) {
out.write("You can read this with a text editor.");
}
}
}
Here, you can open the file file.txt on a text editor and read the message written to it.
File evidence = new File(path);
evidence.mkdirs();
evidence.createNewFile();
File is an abstract concept of a file which does not have to exist. Simply creating a File object does not actually create a physical object.
You can do this in (at least) two ways.
Write something to the file (reference by the abstract File object)
Calling File#createNewFile
You can also create temporary files using File#createTempFile but I don't think this is what you are trying to achieve.
You have only created an object which can represent a file. This is just in memory though. If you want to access the file you must us ea FileInputStream or a FileOutputStream. Then it will also be created on the drive (in case of the outputstream).
FileOutputStream fo = new FileOutputStream(new File(oFileName));
fo.write("test".getBytes());
fo.close();
This is just ur creating file object by using this object u need to call one method i.e createFile() method..
So use evidence.createNewFile(); if you are creating just file.
else if u want to create file in any specific location then specify your file name
i.e File evidence=new File("path");
In this case if ur specifying any directoty
String path="abc.txt";
File file = new File(path);
if (file.createNewFile()) {
System.out.println("File is created");
}
else {
System.out.println("File is already created");
}
FileWriter fw = new FileWriter(file, true);
string ab="Hello";
fw.write(ab);
fw.write(summary);
fw.close();