Issue
Here i will paste what sould be the relevant code to my error and at the end I will explain what doesn't work. I will also paste a picture of what the HTML page looks like.
Also, I cut out what I think should not be needed for this problem finding so that the code would not be even bigger than what I already posted. If needed I will post it all.
FRONT END WITH ANGULAR
FileFrontEnd interface
export interface FileFrontEnd {
name: string;
path: string;
imageBytes: string;
}
FileFrontEnd service
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { environment } from "src/environments/environment";
import { FileFrontEnd } from "./file";
@Injectable({
providedIn: 'root'
})
export class FileService {
private apiServerUrl = environment.apiBaseUrl;//'http://localhost:8080'
constructor(private http: HttpClient) { }
public getFiles(): Observable<FileFrontEnd[]> {
return this.http.get<FileFrontEnd[]>(`${this.apiServerUrl}/file/all`);
}
}
App Component
import { HttpErrorResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { FileFrontEnd } from './file';
import { FileService } from './file.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
title = 'file-web-app';
public files!: FileFrontEnd[];
constructor(private fileService: FileService){}
ngOnInit(): void {
this.getFiles();
}
public getFiles(): void {
this.fileService.getFiles().subscribe(
(response: FileFrontEnd[]) => {
this.files = response;
for(let i = 0; i < this.files.length; i++) {
console.log(this.files[i].imageBytes);
}
},
(error: HttpErrorResponse) => {
alert(error.message);
}
);
}
}
App Component HTML
<table>
<tr>
<th>Name</th>
<th>Path</th>
<th>Icon String</th>
</tr>
<tr>
<td style='cursor: pointer; cursor: hand;' (click)="backPath()">..</td>
<td>home path</td>
<td>No Icon String</td>
</tr>
<tr *ngFor="let file of files" style='cursor: pointer; cursor: hand;' (click)="changePath(file.path)">
<td><img src="data:image/png;base64,{{file.imageBytes}}"> {{file.name}}</td>
<td>{{file.path}}</td>
<td>{{file.imageBytes}}</td>
</tr>
</table>
BACKEND IN JAVA
package com.file.web.fileweb;
import org.springframework.util.Base64Utils;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class FileFrontEnd {
private String name;
private String path;
private String imageBytes;
//private Icon imageIcon;
//constructors
//public FileFrontEnd() { }
public FileFrontEnd(String name, String path, String imageBytes) {
this.name = name;
this.path = path;
this.imageBytes = imageBytes;
//this.imageIcon = imageIcon;
}
//getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPath() { return path; }
public void setPath(String path) { this.path = path; }
public String getImageIcon() { return imageBytes; }
public void setImageIcon(String imageBytes) { this.imageBytes = imageBytes; }
//converter from Icon to byte[] to String
public static String convertToBytes(Icon icon) {
BufferedImage img = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
icon.paintIcon(null,g2d,0,0);
g2d.dispose();
try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
try {
ImageIO.write(img, "png", ios);
// Set a flag to indicate that the write was successful
} finally {
ios.close();
}
byte[] bytes = baos.toByteArray();
//return bytes;
String encodedText = Base64Utils.encodeToString(bytes);
return encodedText;
} catch(IOException ex) {
ex.printStackTrace();
}
return null;
}
}
And this is the GetMapping, where the fileSystem is an object of my calss FileSystem that is needed to get paths and the file icon and it seems to be working fine
@GetMapping("/all")
public ResponseEntity<List<FileFrontEnd>> getAllFiles() {
File[] filesArray = fileSystem.listFiles(fileSystem.getCurrentPath());
//convert... to FileFrontEnd
List<FileFrontEnd> filesFront = new ArrayList<FileFrontEnd>();
for(int i = 0; i < filesArray.length; i++) {
FileFrontEnd f = new FileFrontEnd(filesArray[i].getName(), filesArray[i].getPath(), FileFrontEnd.convertToBytes(fileSystem.getIconImageForPath(filesArray[i])));
filesFront.add(f);
}
return new ResponseEntity<>(filesFront, HttpStatus.OK);
}
Explanation
What this sould do is show an HTML page with a table of files/folders in the currentPath
(folder) that I reached with getCurrentPath()
in the GetMapping. And what I wanted to achieve is to include also the Icon shown in the file explorer of the pc, but on the page.
Problem
My problem is that the imageBytes in FileFrontEnd seems to be undefined (i 'console.log()'-ged it)
in fact in the HTML page {{file.imageBytes}}
doesn't print anything. On the other side the name and path, both strings, work perfectly fine. I thought that suggested the error was in the BackEnd, but I verified that the string FileFrontEnd.convertToBytes(fileSystem.getIconImageForPath(filesArray[i]))
(which is the String
conversion of the byte[]
conversion of the Icon
seen in FileFrontEnd.convertToBytes(Icon icon)
) computes what I want it to.
So I thought there might be some kind of error with the imageBytes
string during the connection between back and front end, but I could not find any documentation on this. (maybe the length of the string idk)
Is somebody able to help with this?
This is how it looks like, where the standard 'error' icons should be replaced with the pc ones.
Solution
The issue is that the getter method in the FileFrontEnd
java class which expose the imageBytes
doesn't match the property name in frontend class:
In java, the property name is imageIcon
which is exposed by the public getter method getImageIcon()
not by the field name imageBytes
:
public String getImageIcon() { return imageBytes; }
However in frontend class you expect:
imageBytes: string;
So, my advice is to change this getter to be like the following so that the right property mapping matches properly:
public String getImageBytes() { return imageBytes; }
Also, you need to change the setter as well to be setImageBytes()
instead of setImageIcon()
This was the solution of undefined string in frontend. However, I'm not sure if you need to display the icon in HTML, you need to change String
to be byte[]
.
Answered By - Moemen
Answer Checked By - Candace Johnson (JavaFixing Volunteer)