Задать вопрос
  • Чем можно открыть VHDX?

    @lunatig
    может кому пригодится.
    если в windows нужно восстановить данные с упавшего wsl дистрибутива.
    то копируете в любое место ext4.vhdx. обязательно! вдруг что то пойдет не так
    C:\Users\UserName\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu20.04.......\LocalState\ ext4.vhdx
    потом просто нужно 2 раза кликнуть на образе и в управлении дисками появится управлении дисками появится новый диск. !!!Больше ничего делать не нужно.
    Далее скачиваем и устанавливаем free-linux-recovery находим этот новый раздел и восстанавливаем все что нужно.
    https://www.r-studio.com/ru/free-linux-recovery/Do...
    Ответ написан
    Комментировать
  • БД для хранения сообщений чата, какую выбрать?

    @InoMono
    Вполне себе любая развитая современная РСУБД годится для этой задачи.
    MySQL, PostgreSQL...

    А по мере роста нагрузки - тут не выбором СУБД нужно заморачиваться, а MQ-сервер ставить. Он гораздо легче сравиться с бешенными нагрузками.

    Как вариант - Queue на базе Tarantool, например. Я даже не знаю что вы там должны такое сделать, чтобы заткнуть его производительность. При условии того, что на сервере достаточно много оперативной памяти.

    Из самого критично подозрительного - полнотекстовый поиск.
    Впрочем, полагаю, что полнотекстового поиска средствами MySQL или PostgreSQL вплоне хватит.

    Если уж делать прям таки серьезный чат типа Slack, то для полнотекстового поиска я бы вообще отдельную специализированную БД держал бы. Например, SphinxSearch.

    Но, для начала, возможностей PostgreSQL или MySQL будет вполне достаточно.

    Что до Mongo... Если вам не нужна репликация без консистентности. Зато быстрая...
    Так вот если вам не нужна такая репликация, то Монга вам не нужна.

    РСУБД будут существенно быстрее.

    Вот ежели вы планируете заводить ваш чат в кластер, когда одного сервера вам не хватит, то тут да, тут РСУБД не лучший выбор. Тут бы я рекомендовал как раз Монгу.
    Но опять таки кластер серверов для чата вы без MQ не сделайте.

    Вывод:

    Начните с обычной РСУБД.
    Как начнутся затыки - рассмотрите MQ
    Как начнется рост до масштаба планеты - рассматрите Монгу.

    Вся система работает с бд MySQL - InnoDB, сообщения пишутся в бд при каждой отправке (INSERT), пока сервис еще не запущен, сообщений мало (только мои тестовые) все работает шустро, но вот когда запущу и количество сообщений перевалит за несколько миллионов, что будет тогда с моей бд? Начнутся жесткие тормоза при select и insert?


    Вам никто не мешает это проверить.
    Сгенерируйте миллион случайных сообщений.

    При грамотном использовании индексов - ровным счетом никаких проблем ни на миллионах ни на миллиардах записей.
    Ответ написан
    15 комментариев
  • Как работать с изображениями в веб-приложениях на 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 комментария
  • Когда стоит вкладывать input в label, а когда нет?

    KulakovAngel
    @KulakovAngel
    Full Stack Developer (Node.JS)
    В целом, с точки зрения правильности/валидности кода, как заметил Danny Arty, разницы нет. В примитивных случаях верстки визуальной разницы тоже особо не наблюдается.

    Но если нужно сверстать что-то посложнее, сделать более интересное оформление, разница появляется, поскольку нам нужно взаимодействовать с разным порядком и вложенностью различных тегов. Сравните:

    1. В таком варианте у нас имеется два рядом расположенных тега. Чтобы выбрать второй, идущий после первого, можно использовать "соседский" селектор "+" (Label идет сразу после Input). Данный вариант более "независимый", так как мы можем менять местами input и label, и даже разносить их на удаленное расстояние.
    <style>
      input + label {
        color: #ff0000;
      }
    </style>
    
    <input type="checkbox" id="checkbox" />
    <label for="checkbox">чекбокс</label>


    2. И второй вариант. Здесь мы не можем только средствами html/css обратиться "от input к label", так как не существует способа обратиться от ребенка к родителю (это можно, конечно, легко сделать через JS). Существуют селекторы потомков и соседей, но не родителей. Можно обратиться только от label к input, что дает мало преимуществ, так как
    1. input плохо стилизуется
    2. label обычно ничем не примечателен в отличие от input, который информативен (содержит, например, атрибут type).

    <style>
      label > input  {
        outline-color: #ff0000;
        outline-style: double;
      }
    </style>
    
    <label>
      <input type="checkbox" />
      чекбокс
    </label>


    А теперь представим, что нам нужно сверстать нечто подобное на основе (см. картинку):
    5ffea6239c1da641320430.png

    Как это реализовать? Для label соорудим ":before" и ":after", которым дадим форму скругленного прямоугольника и круга (который, к тому же будет перемещаться).

    Здесь второй вариант нам явно не подойдет, так как мы бы хотели в зависимости от состояния checkbox (.checkbox:checked) изменять родственный ему label:before (изменять цвет, перемещать).
    Если хотите взглянуть подробнее на код, пожалуйста:

    Таким образом, принципиальной разницы нет, но первый вариант (с id) более универсален, так как
    1. input и label могут быть разнесены физически (находится в разных местах документа)
    2. label может идти после input, что дает нам возможность обратиться в CSS от второго к первому

    Надеюсь, ответил на Ваш вопрос.
    Ответ написан
    Комментировать