• Где и как хранить файлы пользователей?

    xez
    @xez
    TL Junior Roo
    1. Адреса должны быть относительными всегда
    2. Место хранения следует задавать переменной
    Ответ написан
    8 комментариев
  • Почему http.server отвечает через две секунды?

    dimonchik2013
    @dimonchik2013
    non progredi est regredi
    SERVER_ADDRESS = 'localhost'
    меняешь на
    SERVER_ADDRESS = '127.0.0.1'

    объяснять нужно? и вооьбще - какую проблему решаем-то?
    Ответ написан
    7 комментариев
  • Какой шаблонизатор Java сейчас востребован? Thymeleaf, Jsp, Freemarker?

    sergey-gornostaev
    @sergey-gornostaev Куратор тега Java
    Седой и строгий
    JSP полезно изучить как часть стандарта JEE, а Thymeleaf как наиболее современный и гибкий из java-шаблонизаторов.
    Ответ написан
    Комментировать
  • Для тех кто пользовался сборками флибустина, стоит ли ставить сборку 10ltsc? Что по стабильности? Как долго пользовались?

    firedragon
    @firedragon
    Не джун-мидл-сеньор, а трус-балбес-бывалый.
    Ищите сборку MSDN. Автор Microsoft.
    Наименьшее число багов хотя и встречаются
    Ответ написан
    1 комментарий
  • Как удалить папки в домашней директории Linux?

    jamakasi666
    @jamakasi666 Куратор тега Linux
    Просто IT'шник.
    Вы не пользуетесь, а софт пользовательский вполне пользуется. Как пример директория downloads будет стартовой если программа выведет диалог сохранения файла(брайзер или телеграм например). Если директория не определена то с шансом 50\50 тебя выкинет в корень системы или корень пользовательской директории.
    Аналогично с templates , если ее нет то во всех файловых менеджерах в пункте меню new file будет пусто, если есть то тот же офисный пакет создаст шаблоны текстовых и табличных файлов.
    Documents аналогично downloads только для офисных пакетов и при сохранении файлов.
    В windows абсолютно идентичная система построена и в домашней директории пользователя также есть эти директории.
    Можно изменить их место назначения если они так сильно мешают. Но без них обретете неудобства в самых неожиданных местах.
    Ответ написан
    Комментировать
  • Как анализировать Java процессы?

    sergey-gornostaev
    @sergey-gornostaev Куратор тега Java
    Седой и строгий
    VisualVM или Java Flight Recorder + Java Mission Control
    Ответ написан
    Комментировать
  • Spring JPA, как обновить данные в модальном окне?

    azerphoenix
    @azerphoenix Куратор тега Spring
    Java Software Engineer
    th:href="@{findOne/(id=${tour.id})}"
    Глянув на этот участок кода могу сказать следующее - не передавайте саму сущность на фронт. Вместо этого используйте паттерн DTO. Т.е. для создания / обновления используйте DTO, который затем в сервисном слое маппите в entity и сохраняете в БД,
    Для маппинга можно использовать разные либы.
    Например, интерфейс Converter<S, T> или же либу MapStruct, ModelMapper и др.
    Также можно использовать BeanUtils.copyProperties() например, для обновления сущности.

    @ResponseBody
        public Tour findOne(Integer id) {
            return tourRepository.findById(id).get();
        }

    Вместо get() лучше использовать например, orElseThrow() и выбросить исключение, которое впоследствиее можно при помощи ExceptionHandler обработать и вернуть корректный http ответ. Например, когда объект не найден (404)

    @Autowired
        private TourRepository tourRepository;
        @PostMapping("/save")
        public String save(@ModelAttribute(name = "tour") Tour tour) {
            tourRepository.save(tour);
            return "redirect:/";
        }
        @PostMapping("/admin/saveTour")
        public String saveTourInModal(@ModelAttribute(name = "tour") Tour tour) {
            tourRepository.save(tour);
            return "redirect:/admin/tourList";
        }

    Нет смысла в двух одинаковых методах внутри одного контроллера. Есть смысл вынести эндпоинты отвечающие за админку в отдельный контроллер.

    Само модальное окно вызывается и заполняется данными объекта. После нажатия на кнопку сохранения, у меня стало выводить исключение org.hibernate.exception.ConstraintViolationException: could not execute statement, и что поля бд title и description не могут быть null.

    Вопрос в том, почему они были null, если вы делаете сабмит формы. Попробуйте глянуть в консоли браузера во вкладке "Сеть", какие именно данные отправляются в бэк. Также в режиме дебага проверьте, что именно вы принимаете и сохраняете в БД.

    Обратите внимание, что на одной странице вы делаете update
    Hibernate: update tour set description=?, end_time=?, image=?, price=?, start_time=?, title=? where code=?

    А на другой странице insert
    Hibernate: insert into tour (description, end_time, image, price, start_time, title) values (?, ?, ?, ?, ?, ?)


    Для более точного ответа нужен исходник проекта, ибо я не вижу кода сущности
    Ответ написан
    Комментировать
  • Какой хостинг лучше использовать для spring приложения?

    sergey-gornostaev
    @sergey-gornostaev Куратор тега Spring
    Седой и строгий
    Любой, предоставляющий VPS/VDS.
    Ответ написан
    3 комментария
  • Как в spring boot + spring security - реализовать форму смены данных пользователя?

    @puppylen Автор вопроса
    Затупил, убрал валидацию в сигнатуре метода контроллера, и через временного пользователя поменял нужные мне поля:
    public String saveUser(@ModelAttribute("currentUser") User user,Model model, Principal principal) {
    		User tempUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    		tempUser.setEmail(user.getEmail());
    		tempUser.setUsername(user.getUsername());
                    userService.save(tempUser);
    Ответ написан
    Комментировать
  • Как telegra.ph понимает, что я - это я?

    @psy667
    Node.js backend developer
    Там используются HttpOnly cookie. То есть сами cookie есть, но в браузере не видны, также к ним нельзя получить доступ из клиентского кода.
    В запросе /check можно увидеть, что в response headers они устанавливаются.
    Set-Cookie: tph_uuid=<token>; expires=Wed, 08 Feb 2023 05:42:55 GMT; path=/; secure; HttpOnly
    Ответ написан
    1 комментарий
  • Как telegra.ph понимает, что я - это я?

    hottabxp
    @hottabxp
    Сначала мы жили бедно, а потом нас обокрали..
    Как тогда Паша понимает, что я - это я?
    Устанавливает тебе печеньки, потом читает их.

    Проблема заключается в том, что ни в локальном хранилище, ни в сессиях, ни в куках нет вообще никаких данных.
    Не совсем верно. Во вкладке "Приложение" их нет, а вот во вкладке "Сеть" по клике на запрос - есть.61fa945a35416058169057.png
    Эти печеньки можно установить в другой профиль/браузер с помощью любого расширения для работы с cookies. Возьмем к примеру расширение Cookie Editor и создадим новую печеньку со следующими параметрами:61fa952a7dcd3486538452.png
    И вуаля, у нас в другом браузере появится кнопочка "Edit'.
    Ответ написан
    Комментировать
  • DigitalOcean как замена хостингу. Как всё грамотно настроить?

    @neol
    Во-первых, безопасность - это процесс, а не состояние.
    Чтобы система была более-менее безопасной, нужно:
    - быть в курсе существующих уязвимостей в используемом софте (как минимум следить за ubuntu security notices )
    - ставить апдейты
    - использовать достаточно стойкие пароли и периодически их менять
    - отключать неиспользуемые сервисы
    - если возможно, максимально ограничивать доступ по сети (например, не выставлять mysql на всеобщее обозрение)
    - отключать неиспользуемые модули, плагины, расширения, фичи etc.
    - делать бекапы
    - всегда строго соблюдать принцип минимальных привилегий
    - не подключаться к серверу с ненадежных систем (подключение с домашнего десктопа, на котором детишки гоняют пиратские игры, а вы смотрите "порно онлайн бесплатно без смс" - не очень хорошая идея).

    Эти довольно простые советы в стиле КО на самом деле дают практически 100% защиту от "автоматических" взломов (имеются в виду боты, которые сканируют все подряд в поисках уязвимых систем и троянчики, вытаскивающие сохраненные пароли). Все остальное больше зависит от вашего кода, чем от каких-то настроек.
    Ответ написан
    Комментировать
  • Почему в связке Spring boot & Angular я должен включать поддержку межсайтовых запросов?

    xez
    @xez
    TL Junior Roo
    Для разработки используйте cross-origin.
    В прод. среде у вас будет какой-то http сервер (nginx, например), который все эти вопросы решит
    Ответ написан
    4 комментария
  • Авторизация и аутентификация пользователя?

    REZ1DENT3
    @REZ1DENT3
    web-developer
    Делайте по инструкции и все получится:
    https://symfony.com/doc/current/security.html
    Ответ написан
    Комментировать
  • Как должен правильно должен выглядеть тест для простого Java приложения?

    @rPman
    Когда ты разрабатываешь какое то приложение, не важно на сколько сложное, ты же не пишешь его с нуля и до конца полностью (точнее такое бывает но редко, для не сильно больших задач и у этого метода есть недостатки - больше подвержен ошибкам), наоборот, код идеологически разбивается на части или этапы разработки, каждый из которых пишется по очереди, до принципиальной возможности запуска, часто кстати в уже написанные части подставляются заглушки (и/или подсовывается база данных в определенном состоянии) и добавляются отладочные сообщения, чтобы приложение запустилось в нужном состоянии чтобы в принципе была возможность протестировать только что написанную часть.

    Под протестировать я подразумеваю любой способ проверить, что получится, как программа отреагирует (например что напишет в логах), и сравнить это с ожидаемым поведением.

    Что значит ожидаемое поведение? когда ты делишь задачу на подзадачи, у тебя появляются промежуточные данные и состояния, ну как в анекдоте про чайник и программиста, чтобы заварить чай нужно:
    * налить воду в чайник
    * поставить на базу/печку и включить
    * дождаться готовности/выключить
    * налить чай
    на каждом этапе нужно проверять состояние, например первый пункт - есть ли вода в чайнике, горячая ли она или холодная, полный ли чайник, есть ли у тебя чайник в руках, и т.п.

    Тесты создают на основе данных, являющихся нормальными, ненормальными и граничными для тех алгоритмов, что используются в коде. К примеру нет нужды проверять температуру воды в чайнике отдельно на фиксированные значения 10, 20, 30,.. градусов, если в алгоритме заложены естественные их границы - холодная и горячая, вот вокруг этих констант и приходится гулять.

    Вот именно эти проверки и есть тесты, которые необходимо автоматизировать.

    Степень полноты тестирования вопрос философский, в конечном счете тесты нужны не только для автоматического тестирования и составления отчетности на изменения кода, но и собственно для разработки, разрабатывая тесты ты делаешь код полнее и точнее (например проверяя температуру воды на первом шаге ты добавишь в код условие - если вода горячая, то пропустить ее нагрев), почитай про test-driven development, TDD

    p.s. а еще тесты - это способ исследовать неизвестную библиотеку, дело в том что open source реалии таковы, что документации нет от слова совсем, в лучшем случае перед тобой список методов с типами аргументов и пара примеров, все остальное нужно догадываться по логике наименований, здравому смыслу и ковырянии в исходниках. Тесты тут могут быть исключительно как способ изучения этой библиотеки, но не обязательно такие тесты реализовывать в коде, это все же процесс в ручную происходит, но все же, создавать тесты на ответы от чужой библиотеки полезно для длительной поддержки проекта для того, чтобы у тебя были алерты на необычное поведение ее после обновления)

    создание правильных и полных тестов очень сильно повышают стоимость разработки и поддержки проекта, причем чуть ли не на порядок, но дает некоторую защиту от смерти проекта в будущем из-за накапливающихся ошибок, о которых ничего не ясно без тестов.

    и еще, создать тесты с нуля на готовый проект почти невозможно.
    Ответ написан
    Комментировать
  • Как обработать большой объем данных?

    gbg
    @gbg
    Любые ответы на любые вопросы
    Мудрый паттерн состоит тупо в том, чтобы не гонять данные из базы туды-сюды. Так что запускайте апдейт прямо в базе.

    Не надо смотреть на модных мальчиков в худи, которые канселят хранимые процедуры и триггеры, и советуют превратить РСУБД в свалку таблиц (и убрать foreingn keys, потому что мешают).
    Ответ написан
    Комментировать
  • Как работать с файловой системой в Spring Boot?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    Но потом, когда я загружу проект на сервер, то там же другая файловая система, другие директории и т.д. - как с этим быть?

    Ну во-первых, как отметил коллега Dmitry Roo нужно использовать относительные пути.
    Во-вторых, у вас должны быть несколько файлов конфигурации (с разными профилями). Например, для dev upload.path будет /home/Desktop/uploads, а для prod профиля будет uploads/ и т.д.

    Во-вторых - а как быть с безопасностью? Как-то же надо фильтровать то, что мне загружают? А то скрипт какой-нибудь загрузят или ещё что-то. Как это вообще отслеживать?

    Обычно, делают проверку MIME type, как на клиентской стороне, так и на стороне сервера.

    Например, средствами js можно запретить загрузку других файлов, кроме image/* и т.д.
    Тоже самое делаю на сервере. Проверяют формат загруженного файла и если он не соответствует ожидаемому MIME type, то выбрасывают исключение и удаляют файл.

    Как отслеживать оставшееся место на диске?

    Смотря, что подразумевается в данном случае. Если хостинг, то java.nio подойдет. А если это amazon s3 или другой сервис, то смотрите документацию к REST API.
    Ответ написан
    2 комментария
  • Как работать с файловой системой в Spring Boot?

    xez
    @xez Куратор тега Java
    TL Junior Roo
    Но потом, когда я загружу проект на сервер, то там же другая файловая система, другие директории и т.д.

    Для этого придумали относительные пути, конфигурационные файлы, симлинки и т.д...

    как быть с безопасностью?
    не давать пользователю загружать что попало. То что пользователь загрузил - никогда не запускать.

    Как отслеживать оставшееся место на диске?

    Java io
    Java nio
    Ответ написан
    4 комментария
  • В Symfony лучше создать одну большую миграцию или много маленьких(отдельную для каждой таблицы)?

    firedragon
    @firedragon
    Не джун-мидл-сеньор, а трус-балбес-бывалый.
    Миграция подразумевает одно изменение одну фичу. Она должна быть отменяемая и содержать все данные.
    Поэтому фича должна быть маленькой, а миграция включать все таблицы которые изменились
    Ответ написан
    Комментировать
  • Как работать с изображениями в веб-приложениях на Java?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    Добрый день!

    Как хранить изображение? Насколько я знаю, их можно запихивать прямо в PostgreSQL, но я склоняюсь в сторону того, что изображения надо сохранять в ресурсы, а в бд хранить только ссылки.

    Не самая лучшая идея хранить картинки в виде binary в БД. Лучше, как вы отметили в БД хранить ссылку на объект, а ресурс в файловой системе. Можно хранить файлы в распределенных файловых системах (AWS, Google), но вряд ли вы располагаете таким бюджетом.

    Вот, простая реализация хранения картинок в FS.

    Entity
    @Entity
    @Data
    @NoArgsConstructor
    @Table(name = "attachments")
    public class Attachment {
    
      @Id
      @GeneratedValue(strategy = GenerationType.SEQUENCE)
      private Long attachId;
    
      private String attachTitle;
    
      @Column(nullable = false, updatable = false)
      private LocalDate uploadDate;
    
      private String extension;
    
      private String downloadLink;
    
    }


    Repository
    @Repository
    public interface AttachmentRepository extends JpaRepository<Attachment, Long> {}


    Service
    public interface AttachmentService {
    
      /**
       * Загрузить новый файл
       *
       * @param file
       * @param user
       * @throws IOException
       */
      Attachment addAttachment(MultipartFile file, User user) throws IOException;
    
    
      /**
       * Найти Вложение по его ID
       *
       * @param attachId
       * @return
       */
      Attachment findAttachById(Long attachId);	
    
      /**
       * Скачать файл
       *
       * @param uploadYear
       * @param fileName
       * @return
       * @throws MalformedURLException
       */
      Resource loadFileAsResource(String uploadYear, String fileName) throws MalformedURLException;
    
    }


    ServiceImpl
    @Service
    @RequiredArgsConstructor
    public class AttachmentServiceImpl implements AttachmentService {
    
      private final AttachmentRepository attachmentRepository;
      private final AppProperties appProperties;
      private final FileTools fileTools;
    
      /**
       * Загрузить новый файл
       *
       * @param file
       * @param user
       * @throws IOException
       */
      @Override
      public Attachment addAttachment(MultipartFile file, User user) throws IOException {
        // Создаем директорию если ее не существует
        File uploadDir = new File(appProperties.getUploadPath());
        // Если директория uploads не существует, то создаем ее
        if (!uploadDir.exists()) {
          uploadDir.mkdirs();
        }
        String curDate = LocalDateTime.now().toString();
        // Создаем уникальное название для файла и загружаем файл
        String fileName =
            "attach_" + curDate + "_" + file.getOriginalFilename().toLowerCase().replaceAll(" ", "-");
        file.transferTo(new File(uploadDir + "/" + fileName));
        Attachment attachment = Attachment.builder()
            .attachTitle(fileName)
            .uploadDate(LocalDate.now())
            .extension(fileTools.getFileExtension(file.getOriginalFilename()))
            .downloadLink("/attachments/get/" + Year.now() + "/" + fileName)
            .build();
        attachmentRepository.save(attachment);
        return attachment;
      }
    
    
      /**
       * Найти Вложение по его ID
       *
       * @param attachId
       * @return
       */
      @Override
      public Attachment findAttachById(Long attachId) {
        return attachmentRepository
            .findById(attachId)
            .orElseThrow(() -> new AttachmentNotFoundException("Attachment not found!"));
      }
    
    
      /**
       * Скачать файл
       *
       * @param fileName
       * @return
       * @throws MalformedURLException
       */
      @Override
      public Resource loadFileAsResource( String fileName)
          throws MalformedURLException {
        Path fileStorageLocation =
            Paths.get(appProperties.getUploadPath()).toAbsolutePath().normalize();
        Path filePath = fileStorageLocation.resolve(fileName).normalize();
        return new UrlResource(filePath.toUri());
      }
    
    }


    Conroller
    @Controller
    @RequiredArgsConstructor
    @RequestMapping("/attachments")
    public class AttachmentController {
    
      private final AttachmentService attachmentService;
      private final UserService userService;
    
      /**
       * Загрузить новое вложение
       *
       * @param file
       * @return
       * @throws IOException
       */
      @PostMapping(value = "/add", produces = "application/json")
      @ResponseBody
      public ResponseEntity<Map<String, String>> uploadAttachment(
          @RequestPart(value = "file") MultipartFile file)
          throws IOException {
        Attachment attachment = attachmentService.addAttachment(file);
        Map<String, String> attachmentStatus = new HashMap<>();
        attachmentStatus.put("status", "ok");
        attachmentStatus.put("attachId", attachment.getAttachId().toString());
        return ResponseEntity.ok(attachmentStatus);
      }
    
      /**
       * Получить ссылку на скачивание загруженного файла
       *
       * @param filename
       * @param request
       * @return
       * @throws IOException
       */
      @GetMapping("/get/{filename:.+}")
      public ResponseEntity<Resource> serveFile(
           @PathVariable String filename, HttpServletRequest request)
          throws IOException {
        Resource resource = attachmentService.loadFileAsResource(filename);
        String contentType;
        contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
        if (contentType == null) {
          contentType = "application/octet-stream";
        }
        return ResponseEntity.ok()
            .contentType(MediaType.parseMediaType(contentType))
            .header(
                HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=\"" + resource.getFilename() + "\"")
            .body(resource);
      }
    }
    Ответ написан
    4 комментария