Search code examples
javareplacenewlinefilewriterbufferedwriter

How can I Write contents in the nextline within a File? and Finding a String inside a File and Replacing it at the same exact line


I am using a SpringBoot application and have introduced a "Like" button for Dog Pictures displayed and upon each Like it will hit the below @RequestMapping(value="likedDog") i am Concatenating the necessary information with a delimiter.

Method Inside the Controller shown below:

@RequestMapping(value = "likedDog")
    public String likedDogList(@RequestParam("breedType") String breedType, @RequestParam("id") String id, Model model){

        Long likes=0L;
        String delimiter="_!_";



        Dogs dogObject= dogsService.returnDogById(Long.valueOf(id));
        if(dogObject.getLikes()==null){
        likes=0L;
        }
        else{
            likes=dogObject.getLikes();
        }


        likes=likes+1;//1,2
        dogObject.setLikes(likes);//set 1,2
        dogObject=dogsService.saveDog(dogObject);

        model.addAttribute("dogObj",dogObject);
        model.addAttribute("dogBreedType",breedType);

        String content=id+delimiter+breedType+delimiter+dogObject.getImgURL()+delimiter+dogObject.getLikes();
        String contentSub=id+delimiter+breedType+delimiter+dogObject.getImgURL();



        try{
        File file = new File("src/main/resources/data/DogLikes.txt");


        if (!file.exists()) {
            file.createNewFile();
        }

        FileWriter fw = new FileWriter(file.getAbsoluteFile());
        BufferedWriter bw = new BufferedWriter(fw);

        bw.write(content);
        bw.newLine();
        bw.flush();     //  ORIGINAL
        bw.close();
        }
        catch(Exception e){
            System.out.println("Exception occured in likedDogList()");
            e.printStackTrace();
        }


        return "liked";
    }

I want to Write the Concatenated String "content" on a newline in the respective file if likes belong to different dogs, the code is shown below, even though I introduce bw.newLine i cant write it in newline instead it simply replaces the old existing content in the first line of the file.

Contents in the File DogLikes.txt shown below:

17_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_1 //but on every click the contents are being replaced in this first line only

Expected Output, the contents to be written into DogLikes.txt on every Like is shown below:

17_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_1
18_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_1 //should be written like this in newline on like of dogId 18
19_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_1 //should be written like this in newline on like of dogId 19

SECOND PROBLEM: Also if i click "Like" on DogId 18 it should go and replace the second line as follows,

17_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_1
18_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_2 //replacement of String in this exact location is expected
19_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_1

Your help on this is much appreciable, Thanks in advance.

hey @daniel the backup method worked for writing down the contents into the file line by line while i am using a different approach for restoring. However I came up with a new idea to incorporate both likes and dislikes into the same line as shown below:

Expected output on clicking "Like" and "Dislike"

   1_!_Labrador_!_http://i.imgur.com/eE29vX4.png_!_like_!_11_!_dislike_!_5
    2_!_Labrador_!_http://i.imgur.com/xX2AeDR.png_!_like_!_3_!_dislike_!_5
    3_!_Labrador_!_http://i.imgur.com/hBFRUuW.png_!_like_!_1_!_dislike_!_5
    16_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_like_!_119_!_dislike_!_5

I kinda need your help in backup() method as i tried different combinations to update likes and dislikes in the same line, as your approach dynamically updates only likes. Also I'm lookinf forward to sort the dogs images according to the highest likes to lowest. Thank you for your help in advance.


Solution

  • As you described in your question your requirements were to use an in-memory storage and a backup mechanism to a particular file in the file system. I tried a short prototype that makes use of spring mvc and data rest. You can find here: https://github.com/dbubenheim/stackoverflow-40820684

    The most interesting part is the backup and restore process of the data for which I have the following two service classes:

    @Service
    public class DogBackupServiceImpl implements DogBackupService {
    
        @Value("${dog.backup.file}")
        private String dogLikesFile;
    
        private static final String DELIMITER = "_!_";
    
        @Override
        public void backup(final Dog dog) throws IOException {
            final Path dogLikesPath = Paths.get(this.dogLikesFile);
            if (!Files.exists(dogLikesPath)) {
                Files.createDirectories(dogLikesPath.getParent());
                Files.createFile(dogLikesPath);
            }
            final File dogLikesFile = Paths.get(this.dogLikesFile).toFile();
            final StringBuilder stringBuilder = new StringBuilder();
            String currentLine;
            String oldLine = null;
            try (final Scanner scanner = new Scanner(dogLikesFile)) {
                while (scanner.hasNextLine()) {
                    currentLine = scanner.nextLine().trim();
                    stringBuilder.append(currentLine);
                    if (currentLine != null && !currentLine.isEmpty()) {
                        stringBuilder.append("\n");
                    }
                    if (currentLine != null && currentLine.startsWith(Long.toString(dog.getId()))) {
                        oldLine = currentLine;
                    }
                }
            }
            String content = stringBuilder.toString();
            final boolean firstLike = (oldLine == null || oldLine.isEmpty());
            final String updateLine = dog.getId() + DELIMITER + dog.getBreedType() + DELIMITER + dog.getImgUrl() + DELIMITER + dog.getLikes();
            if (!firstLike) {
                content = content.replace(oldLine, updateLine);
            }
            try (final BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(dogLikesFile))) {
                bufferedWriter.write(content);
                if (firstLike) {
                    bufferedWriter.write(updateLine);
                }
                bufferedWriter.close();
            }
        }
    }
    

    and

    @Service
    public class DogRestoreServiceImpl extends ContextLoaderListener implements DogRestoreService {
    
        @Value("${dog.backup.file}")
        private String dogLikesFile;
    
        private static final int FIELD_ID = 0;
        private static final int FIELD_BREED_TYPE = 1;
        private static final int FIELD_IMG_URL = 2;
        private static final int FIELD_LIKES = 3;
        private static final String DELIMITER = "_!_";
    
        private final DogRepository dogRepository;
    
        public DogRestoreServiceImpl(final DogRepository dogRepository) {
            this.dogRepository = requireNonNull(dogRepository, "dogRepository must not be null!");
        }
    
        @Override
        public void restore() throws IOException {
            final Path path = Paths.get(this.dogLikesFile);
            if (!Files.exists(path)) {
                System.out.println("nothing to restore");
                return;
            }
            final File dogLikesFile = path.toFile();
            final Set<Dog> dogs = new HashSet<>();
            String line;
            try (final BufferedReader reader = new BufferedReader(new FileReader(dogLikesFile))) {
                while ((line = reader.readLine()) != null) {
                    final String[] fields = line.split(DELIMITER);
                    final Dog dog = new Dog.DogBuilder(Long.parseLong(fields[FIELD_ID]))
                        .breedType(fields[FIELD_BREED_TYPE])
                        .imgUrl(fields[FIELD_IMG_URL])
                        .likes(Long.parseLong(fields[FIELD_LIKES]))
                        .build();
                    dogs.add(dog);
                }
                this.dogRepository.save(dogs);
            }
        }
    
        @Override
        public void contextInitialized(final ServletContextEvent event) {
            try {
                System.out.println("context initialized. restoring data...");
                this.restore();
                System.out.println("data restoring completed!");
            } catch (final IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }    
    

    The idea is: The backup process saves every time the spring data repository saves an enitity to keep the backup file up to date. The restore process is basically triggered when the spring context is successfully initialized (when the application starts). So you should be able to have all the data both in memory and in the backup file.

    You can interact with the service for example via cURL with the following basic actions:

    # retrieve all dogs
    curl -X GET "http://localhost:8080/dogs"
    
    # post dog
    curl -X POST -H "Content-Type: application/json" -d '{
        "breedType": "Husky",
        "imgUrl": "http://i.imgur.com/abc123.png",
        "likes": 0
    }' "http://localhost:8080/dogs"
    
    # like dog
    curl -X POST -H "Content-Type: application/json" -d '' http://localhost:8080/dogs/likeDog/<dogs-id>"
    

    Hope that helps! If there are any further questions let me know.