I receive an XML file from a client. I've another file containing Base-64 encoded data which I embed in one of the elements in XML file. After doing all this merging, I need to return the content of file either as string or a DOM object, returning as InputStream will not work.
But the resulting merged file has null character at the end which is causing issues when file is processed as XML. How can I get rid of it. This is how I am merging my files.
public Object merge(List<File> files) throws Exception {
System.out.println("merge with arguments is called");
if(files == null || files.isEmpty() || files.size()<2){
throw new IllegalArgumentException("File list cannot be null/empty and minimum 2 files are expected");
}
File imageFile = getImageFile(files);
File indexFile = getIndexFile(files);
File inProcessFile = new File("temp/" + indexFile.getName().replaceFirst("[.][^.]+$", "") + ".xml");
File base64EncodedFile = toBase64(imageFile);
/* Write from index file everything till attachment data to inProcess file*/
Scanner scanner = new Scanner(indexFile).useDelimiter("\\s*<AttachmentData>\\s*");
FileWriter writer = new FileWriter(inProcessFile);
writer.append(scanner.next());
/* Write <AttachmentData> element into inProcess file */
writer.append("<AttachmentData>");
/* Write base64 encoded image data into inProcess file */
IOUtils.copy(new FileInputStream(base64EncodedFile), writer);
/* Write all data from </AttachmentData> element from index file into inProcess file */
String fileAsString = IOUtils.toString(new BufferedInputStream(new FileInputStream(indexFile)));
String afterAttachmentData = fileAsString.substring(fileAsString.indexOf("</AttachmentData>"));
InputStream input = IOUtils.toInputStream(afterAttachmentData);
IOUtils.copy(input, writer);
/* Flush the file, processing completed */
writer.flush();
writer.close();
System.out.println("Process completed");
}
private File getIndexFile(List<File> files) {
for(File file:files){
String extension = FilenameUtils.getExtension(file.getName());
if(extension.equalsIgnoreCase(IDX_FILE_EXT))
return file;
}
throw new IllegalArgumentException("Index file doesn't exist or cannot be read.");
}
private File getImageFile(List<File> files) {
for(File file:files){
String extension = FilenameUtils.getExtension(file.getName());
if(extension.equalsIgnoreCase(IMG_FILE_EXT))
return file;
}
throw new IllegalArgumentException("Image file doesn't exist or cannot be read.");
}
private File toBase64(File imageFile) throws Exception {
System.out.println("toBase64 is called");
Base64InputStream in = new Base64InputStream(new FileInputStream(imageFile), true);
File f = new File("/temp/" + imageFile.getName().replaceFirst("[.][^.]+$", "") + ".txt");
Writer out = new FileWriter(f);
IOUtils.copy(in, out);
return f;
}
Please help me understand how I can fix my code which produces null character
Fix the code that produces it, perhaps by removing part or the whole of it. To find this out, you should ask yourself the following questions:
Is the null character already present in the original XML file received from the client?
In what position of the XML document does the element that contains the base-64 data appear?
In what position of the XML document does the null character appear?
Are you decoding the base-64 file in any form?
Does the base-64 file contain the null character?
If yes, why?
What method is used to "merge" the base-64-encoded data into the XML document?
As per information produced later by the OP, and if the file always contains the null character, the simplest solution is to replace line:
String afterAttachmentData = fileAsString.substring(fileAsString.indexOf("</AttachmentData>"));
with
String afterAttachmentData = fileAsString.substring(fileAsString.indexOf("</AttachmentData>"),fileAsString.length()-1);
However, in the long run it's much better to check with the client if the null character is generated on their end of things and, if so, suggest them to to correct the code that generates it so the XML document is valid.
Related
I have a zip archive that contains several gzip files. But gzip file's extentions are also .zip . I walk through zip archive with ZipInputStream. How can I detect inner file's type with reading its content rather than extentions. I also need not to change (or reset) ZipInputStream position.
So I need;
Read files in zip with using inputStream (ZipInputStream in my case) Because zip in zip is possible.
Find file type from its content.
While finding file type from its content, inputStream position should not change. Because i will continue to read next files.
Example:
root/1.zip/2.zip/3.zip(actually 3 is gzip)/4.txt
Sample Java Code:
public static void main(String[] args) {
//root/1.zip/2.zip/3.zip(actually 3 is gzip)/4.txt
String file = "root/1.zip";
File rootZip = new File(file);
try (FileInputStream fis = new FileInputStream(rootZip)) {
lookupInZip(fis)
.stream()
.forEach(System.out::println);
} catch (IOException e) {
System.out.println("Failed to get files");
}
}
public static List<String> lookupInZip(InputStream inputStream) throws IOException {
Tika tika = new Tika();
List<String> paths = new ArrayList<>();
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
ZipEntry entry = zipInputStream.getNextEntry();
while (entry != null) {
String entryName = entry.getName();
if (!entry.isDirectory()) {
//Option 1
//String fileType = tika.detect(entryName);
//Option 2
String fileType = tika.detect(zipInputStream);
if ("application/zip".equals(fileType)) {
List<String> innerPaths = lookupInZip(zipInputStream);
paths.addAll(innerPaths);
} else {
paths.add(entryName);
}
}
entry = zipInputStream.getNextEntry();
}
return paths;
}
If I use option 1, '3.zip' is evaluated as zip file but it is gzip.
If I use option 2, '2.zip' is evaluated as zip correctly by using its content. But when lookupInZip() is called for '3.zip' recursively, zipInputStream.getNextEntry() returns null. Because in previous step, we use inputStream content to detect type and inputStrem position changed.
Note: tika.detect() uses BufferedInputStream in implementation to reset inputStream position but it does not solve my problem.
The first two bytes are enough to see if it is likely a zip file, likely a gzip file, or certainly something else.
If the first two bytes are 0x50 0x4b, then it is likely a zip file. If the first two bytes are 0x1f 0x8b, then it is likely a gzip file. If it is neither, then the file is something else.
The first two bytes matching is not a guarantee it is that type, but it appears from your structure that it is usually one or the other, and you can use the extension as further corroborating evidence that it is compressed.
As for not changing the position, you need a way to peek at the first two bytes without advancing the position, or a way to get them and then unget them to return the position to where it was.
I want to append to the file and if its not empty; and want to write if its empty. Below is is my code. write function works, append is not. Can anyone guide here?
public class Filecreate {
public static void main(String args[]) throws IOException {
File file = new File("newFileCreated.txt");
System.out.println("file path "+file.getAbsolutePath() +" file length - "+file.length());
FileWriter myWriter = new FileWriter(file);
if((int)file.length() != 0){
myWriter.append("appended text\n");
}else{
myWriter.write("Files in Java might be tricky, but it is fun enough!");
}
myWriter.close();
System.out.println("file length after writing to file "+file.length());
}
}
You don't need to worry about whether or not the file contains anything. Just apply the argument of true to the append parameter in the FileWriter constructor then always use the Writer#append() method, for example:
String ls = System.lineSeparator();
String file = "MyFile.txt";
FileWriter myWriter = new FileWriter(file, true)
myWriter.append("appended text" + ls);
/* Immediately write the stream to file. Only really
required if the writes are in a loop of some kind
and you want to see the write results right away.
The close() method also flushes the stream to file
before the close takes place. */
myWriter.flush();
System.out.println("File length after writing to file " +
new File(file).length());
myWriter .close();
If the file doesn't already exist it will be automatically created
and the line appended to it.
If the file is created but is empty then the line is appended to it.
If the file does contain content then the line is merely appended to
that content.
The issue occurs because you measure file's size after you open it. Thus, you have to check file's size before you open it. Also, I won't recommend to cast long to int, because your solution won't work on big files. To conclude, following code will work for you:
public static void main(String[] args) throws IOException {
File file = new File("newFileCreated.txt");
long fileSize = file.length();
System.out.println("file path "+file.getAbsolutePath() +" file length - "+file.length());
FileWriter myWriter = new FileWriter(file);
if(fileSize > 0L){
myWriter.append("appended text\n");
}else{
myWriter.write("Files in Java might be tricky, but it is fun enough!");
}
myWriter.close();
System.out.println("file length after writing to file "+file.length());
}
I am writing a web app that I would like to be able to import a csv file into a database from a jsp. Previously I have used the following code to insert the csv into the database.
LOAD DATA LOCAL INFILE "/myFileLocation.csv"
INTO TABLE myTable
COLUMNS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
ESCAPED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 LINES;
Which works great when I have the file locally.
My question, when I upload the csv file in my jsp as a multipart. Is it possible for me to pass that PartItem file as a variable and replace the "/myFileLocation.csv" with the PartItem files temp location?
I can see the temp location when I debug and view the PartItem file which resides in repository/path under the variables table. Is this at all possible to access or do i need to parse the csv and insert it into the database that way?
I ended up finding a way to make this work. Not sure if it's the best solution but its working as I envisioned. Basically I create a string pointing to an assets folder I created in the web/resources like this.
final String mainPath = this.getServletContext().getRealPath("resources/assets");
Then I read the uploaded file, check to see if the file already exists in my assets folder, if it does I delete it.
Part filePart = request.getPart("csvFile");
String path = mainPath + "/" + filePart.getSubmittedFileName();
File fileTemp = new File(path);
if(fileTemp.exists()){
fileTemp.delete();
}
Lastly I read the uploaded file and write a new file in the location I directed it to which in this case is the assets folder I created like this.
final String fileName = filePart.getSubmittedFileName();
File convFile = new File(filePart.getSubmittedFileName());
FileOutputStream fos = new FileOutputStream(convFile);
OutputStream out = null;
InputStream filecontent= null;
try{
out = new FileOutputStream(new File(mainPath + File.separator + fileName));
filecontent = filePart.getInputStream();
int read = 0;
final byte[] bytes = new byte[1024];
while((read = filecontent.read(bytes)) != -1){
out.write(bytes, 0, read);
}
} catch (FileNotFoundException fne) {
} finally {
if (out != null) {
out.close();
}
if (filecontent != null) {
filecontent.close();
}
}
After that I just passed a string containing the path to the file with the file name to the DAO I created where I was able to utilize the sql statement I had posted above.
Like I stated before, not sure if this is the best way to do this but it seems to be working fine for me and none of my java code is contained within my jsp. If anyone has a better way of doing this or sees something wrong with what I did here let me know, I'd be very interested to hear about it.
I have a UTF-8 text file example.txt that contains:
c:/temp/file.txt
I read the file content using this method:
public static String fileToString(final File file, final String charset) throws AppServerException
{
final byte[] buffer = new byte[(int) file.length()];
FileInputStream fileInputStream = null;
try
{
fileInputStream = new FileInputStream(file);
fileInputStream.read(buffer);
}
catch (final FileNotFoundException e)
{
throw new AppServerException(e.getMessage());
}
catch (final IOException e)
{
throw new AppServerException(e.getMessage());
}
finally
{
FileHelper.close(fileInputStream);
}
try
{
return new String(buffer,charset);
}
catch (UnsupportedEncodingException e)
{
throw new AppServerException(e.getMessage());
}
}
Then I want to check if the file c:/temp/file.txt exists:
String content = fileToString("example.txt","UTF8");
File file = new File(content );
System.out.println(file.exists());
The exits() return false but the file actually exists.
If I change the encoding of example.txt to ANSI using notepad++, the exists() return true.
I already tried using:
"c:\temp\file.txt",
"c:\\temp\\file.txt",
"c:\\\\temp\\\\file.txt",
but without success.
I really need to use the file as UTF8. Do you have tips so the method exists() returns true?
Notepad++ probably puts a Byte Order Mark in front of the file. This is unnecessary for UTF-8 and Java does not interpret this sequence of three characters.
Either use an editor that does not use a Byte Order Mark or write the string in ANSI if your filename does not contain any non-ASCII characters.
Perhaps the file is not actually encoded as UTF-8. Can you print the actual byte values of the "\" characters in the file?
While you are at it: InputStream.read(byte[] b) is not guaranteed to read b.length bytes from the stream. You should be reading in a loop and checking the return value of the read() method in order to see how many bytes were actually read in each call.
I am using Eclipse. I want to read number of XML files from a directory. Each XML file contains multiple body tags. I want to extract values of all the body tags. My problem is I have to save each body tag value (text) in a separate .txt file and add these text files in another given directory. Can you plz help how can I create dynamically .txt file and add them in a specified directory?
Thanks in advance.
First specify directory path and name
File dir=new File("Path to base dir");
if(!dir.exists){
dir.mkdir();}
//then generate File name
String fileName="generate required fileName";
File tagFile=new File(dir,fileName+".txt");
if(!tagFile.exists()){
tagFile.createNewFile();
}
add import for java.io.File;
File f;
f=new File("myfile.txt");
if(!f.exists()){
f.createNewFile();
replace "myfile.txt" to path to file you needed and file will be created when you say
e.g. "c:\\somedir\\yourfile.txt"
It's not clear why you have mentioned the XML part. But it seems that you are able to get the text from XML file and wanted to write to separate text file.
Please go through this basic tutorial for creating, reading and writing files in Java: http://download.oracle.com/javase/tutorial/essential/io/file.html
Path logfile = ...;
//Convert the string to a byte array.
String s = ...;
byte data[] = s.getBytes();
OutputStream out = null;
try {
out = new BufferedOutputStream(logfile.newOutputStream(CREATE, APPEND));
...
out.write(data, 0, data.length);
} catch (IOException x) {
System.err.println(x);
} finally {
if (out != null) {
out.flush();
out.close();
}
}
Do something like this.
try {
//Specify directory
String directory = //TODO....
//Specify filename
String filename= //TODO....
// Create file
FileWriter fstream = new FileWriter(directory+filename+".txt");
BufferedWriter out = new BufferedWriter(fstream);
//insert your xml content here
out.write("your xml content");
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
} finally {
//Close the output stream
out.close();
}